Three changes, all about values that do not do what they promise:
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level.
2. Ingress gate (#93): the Ingress template is wrapped in
{{- if .Values.ingress.enabled }} and the key is added to values.yaml
with default true, so behaviour is unchanged.
The domain checkpoint from rallly ran over values.yaml and templates
and came back empty here: ingress.domain is read nowhere but in the
Ingress template. Unlike rallly, zipline, voucher-vault and outline,
this gate therefore frees both domain and clusterIssuer, and no extra
required() is needed.
3. Null-safety for hardening, probes and resources (#99).
This chart's hardening is a documented deviation from the repo
standard, and the fallbacks reproduce it exactly instead of restoring
the standard: no runAs* values (s6-overlay boots as root and drops to
PUID/PGID 1000), readOnlyRootFilesystem stays false (read-only mode
disables the PUID/PGID remapping, silently moving the runtime uid from
1000 to 911 and breaking existing volume data), and capabilities.add
keeps its six entries that the s6 init and supervision need. A
"cleaned up" standard default here would not be tidier, it would be an
outage.
cwa.storage stays deliberately not null-safe: an erased value yields an
invalid PVC, which is the loud class #99 excludes.
Rendering with normal values is byte-identical to 4.0.0.
No ingress gate: this chart has no Ingress at all (game and messaging
ports are exposed via NodePort), and ingress.domain appears neither in
its templates nor in its values.
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level. Like teamspeak-server, this chart keeps env,
probes and securityContext at the TOP level, and the schema mirrors
that shape.
2. Null-safety for hardening, probes and resources (#99). What a rebuilt
default must not lose here:
- liveness and startup run the image's own healthcheck script via
exec. It queries the server's HTTPS API on SERVERGAMEPORT, so it
survives a port override, which a tcpSocket default could not.
- readiness keeps the named "api" port.
- the resources default keeps limitFactor 2 rather than the repo-wide
3, which is what holds the memory limit at the published 8Gi.
- the pod context keeps fsGroupChangePolicy OnRootMismatch, which is
what avoids a recursive chown of multi-GB game files on every start.
server.storage stays deliberately not null-safe: an erased value yields
an invalid PVC, which is the loud class #99 excludes.
This chart is excluded from the CI install-test, so the local proof is
the only one. Rendering is byte-identical to 4.0.0 with default values
and with the production bundle values, and the rendered manifests were
additionally validated by the cluster's API server via a server-side
dry-run.
No ingress gate: this chart has no Ingress at all (voice/file-transfer
are exposed via NodePort), and ingress.domain appears neither in its
templates nor in its values.
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level. Note that this chart keeps replicas,
resources, probes and securityContext at the TOP level rather than
under a chart-named key, so the schema mirrors that shape.
2. Null-safety for hardening, probes and resources (#99). The rebuilt
probes keep the "file-transfer" port name: the voice service is UDP
and cannot be probed, so that TCP port is the only thing a probe can
target - a default probe without it would leave the workload with no
working probe at all.
The container defaults keep capabilities.add as an empty list, exactly
as values.yaml has it, so an erased block restores today's rendering
byte for byte rather than a cleaned-up variant.
storage stays deliberately not null-safe: an erased block yields an
invalid PVC, which is the loud class #99 excludes.
Rendering with normal values is byte-identical to 4.0.0.
No ingress gate: this chart already gates its Ingress correctly. Two
changes plus one guard the existing gate turned out to need.
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level.
2. Null-safety for hardening, probes and resources (#99). All three
probes keep /_health when rebuilt: it is Outline's only health
endpoint, and "/" returns 500 as well when the database is down, so
there is no dependency-free path to fall back to.
3. ingress.domain is required() where the StatefulSet reads it. It feeds
the URL env var - Outline's public URL for generated links and the
OIDC redirect URI - and did so without a guard. With the existing gate
and no domain it rendered "https://", breaking logins silently. That
hole predates this change; it now fails loudly.
outline.storage stays deliberately not null-safe: an erased block yields
an invalid PVC, which is the loud class #99 excludes.
Apart from an added comment, the rendered manifest is unchanged.
No ingress gate: this chart already gates its Ingress correctly. Two
changes plus one guard that the existing gate turned out to need.
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level. Lists that go verbatim into the pod spec
(ports, storage.volumes, storage.volumeMounts) stay open.
2. Null-safety for hardening, probes and resources (#99). Two specifics
of this chart:
- The probes were wrapped in "if" guards, so an erased probe block did
not render "livenessProbe: null" - it removed the probe entirely and
silently. The guards are gone; an empty block now means chart
defaults, so the workload keeps its probes.
- The rebuilt probes keep the Host: 127.0.0.1 header. Django's
ALLOWED_HOSTS only accepts 127.0.0.1 and the configured DOMAIN, so a
probe without it would get 400 and the pod would never be ready.
3. ingress.domain is required() where it is read. The dependency lives in
values.yaml, not in a template: the DOMAIN env entry is a tpl string
feeding Django's ALLOWED_HOSTS. With the existing gate and no domain
the app rendered DOMAIN: "" and answered 400 to every real request -
silently, and already before this change. It now fails loudly.
Apart from an added comment, the rendered manifest is unchanged.
Three changes, all about values that do not do what they promise:
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level, so a key the chart does not read is rejected
at render time instead of being silently ignored.
2. Ingress gate (#93): the Ingress template is wrapped in
{{- if .Values.ingress.enabled }} and the key is added to values.yaml
with default true, so behaviour is unchanged.
As in rallly, ingress.domain is not a pure Ingress value: the
StatefulSet also feeds it to CORE_DEFAULT_DOMAIN, the app's default
domain for generated share links, and did so without required(). The
guarantee only came from the ungated Ingress template, so the gate
would have handed out unusable links silently. That read is now
required() as well; only clusterIssuer becomes unnecessary.
3. Null-safety for hardening, probes and resources (#99): an erased block
fell back to "securityContext: null" (unhardened), a vanished probe or
a container without resource accounting. The rebuilt liveness probe
keeps "/" rather than /api/healthcheck, so a database outage still does
not restart the pod.
config stays deliberately not null-safe: storage and storageClass are
required(), which puts an erased block in the loud class #99 excludes.
Apart from an added comment, the rendered manifest is unchanged.
Three changes, all about values that do not do what they promise:
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level, so a key the chart does not read is rejected
at render time instead of being silently ignored.
2. Ingress gate (#93): the Ingress template is wrapped in
{{- if .Values.ingress.enabled }} and the key is added to values.yaml
with default true, so behaviour is unchanged.
ingress.domain is NOT a pure Ingress value here - it is also the app's
public base URL (NEXT_PUBLIC_BASE_URL). Gating alone would therefore
have introduced a new silent failure: with the Ingress switched off and
no domain, the deployment rendered value: "https://" and every
generated link would be broken. The deployment's read of the value is
now required(), so that case fails loudly. Only clusterIssuer actually
becomes unnecessary when the Ingress is off.
3. Null-safety for hardening, probes and resources (#99): an erased block
fell back to "securityContext: null" (unhardened), a vanished probe or
a container without resource accounting. Restoring the pod context
matters especially here, because its fsGroup is what makes the Next.js
cache emptyDir group-writable for the container.
rallly.cache is deliberately NOT null-safe: cache.path is required(), so
an erased block fails loudly and belongs to the loud class #99 excludes.
Apart from an added comment, the rendered manifest is unchanged.
Records the reasoning inline, matching the comment the gotenberg chart
carries, so the next edit does not quietly switch back to the raw helper and
drop the paragraph whenever a resources: block has been erased.
Tika has no Ingress, so the notes lead with the cluster-internal URL, the
Service-port-80 to container-port-9998 mismatch a consumer has to get right,
and /parsers as the direct answer to "why did this file come back empty".
Two warnings come from measuring the running container rather than from
reading the values file:
- The image tag has to keep its "-full" suffix. That variant is where
Tesseract 5.5.0 and the extra parsers live; the plain image carries no
tesseract binary at all, so dropping the suffix does not fail loudly, it
just stops OCR and returns empty text for scans.
- The JVM never sees the container limit. It starts without -Xmx and derives
its heap from the limit at MaxRAMPercentage=25, so the number worth sizing
against is a quarter of it.
The heap figure is computed from the effective limit through the chart's own
null-safe effectiveResources helper, so an erased resources: block still
prints the defaults the container actually receives instead of dropping the
paragraph. Refs #43.
Retrofit of #99 for a chart that already carries a values.schema.json,
plus the schema type change that goes with it.
An override written without children is YAML null. Where the chart
defines a default, Helm's coalescing deletes the default before schema
validation runs, so the schema never sees the null and cannot reject it:
the container rendered "securityContext: null" - unhardened, healthy,
silent. Where the chart defines no default (resources.limits), the null
survives into validation and a strict "object" type aborted the render.
The defaultedDict helper from #96 handles the first case; widening the
types of exactly these blocks to ["object", "null"] handles the second.
All other keys stay strict, additionalProperties: false is untouched.
The probes address the container port by name, so a rebuilt default probe
needs no further context.
Rendering with normal values is byte-identical to 5.0.0.
Retrofit of #99 for a chart that already carries a values.schema.json,
plus the schema type change that goes with it.
An override written without children is YAML null. Where the chart
defines a default, Helm's coalescing deletes the default before schema
validation runs, so the schema never sees the null and cannot reject it:
the container rendered "securityContext: null" - unhardened, healthy,
silent. Where the chart defines no default (resources.limits), the null
survives into validation and a strict "object" type aborted the render.
The defaultedDict helper from #96 handles the first case; widening the
types of exactly these blocks to ["object", "null"] handles the second.
All other keys stay strict, additionalProperties: false is untouched.
The probes address the container port by name, so a rebuilt default probe
needs no further context.
Rendering with normal values is byte-identical to 8.0.0.
Gotenberg has no Ingress, so the notes lead with the cluster-internal URL and
point out that the Service listens on 80 while the container listens on 3000
— the mismatch a consumer such as paperless-ngx has to get right — plus the
port-forward command for reaching it by hand.
Three findings from measuring the running chart went in as warnings:
- The chart passes no command-line flags, so Gotenberg's basic auth, OIDC,
SSRF guards and request timeout cannot be enabled through values at all.
- Memory, not disk, is the first limit to hit: at the default limit a large
document gets the container OOM-killed, the caller sees the connection
dropped rather than an HTTP error, and the notes give the exact kubectl
command that prints OOMKilled.
- Temp space is an emptyDir counted against ephemeral-storage, where running
out looks different — the pod is evicted and replaced, not restarted.
Both limits are read back through the chart's own resources helper, so the
printed figures follow limitFactor and any override instead of being
hardcoded. Refs #43.
Three changes, all about values that do not do what they promise:
1. Ingress gate (#93): the Ingress template is wrapped in
{{- if .Values.ingress.enabled }}. The key already existed in
values.yaml with default true but was never read, so switching it off
did nothing. Both required() calls for domain and clusterIssuer live
inside the Ingress file, so gating also removes the contradiction that
one had to supply a domain and issuer in order to turn off an Ingress
one does not want.
2. Null-safety for hardening, probes and resources (#99): an override
written without children is YAML null and erased the whole default
sub-tree, rendering "securityContext: null" - an unhardened container
that starts and reports healthy.
3. values.schema.json types (#68 + #99): the blocks covered by the
null-safety accept null. This is load-bearing only for keys that have
no chart default (e.g. resources.limits): there Helm has nothing to
delete, so the null survives into schema validation and a strict
"object" type rejected it outright.
Rendering with normal values is byte-identical to 7.0.0.
Prints what an operator would otherwise have to look up after installing:
the rendered Ingress URL, the cert-manager Certificate to watch while the
browser still warns, and the fact that the chart needs no secrets and no
volumes.
Adds three warnings that the values alone do not tell: CyberChef ships no
authentication, so everything that reaches the URL can use it; the app is
static and holds no server-side state, so upgrades and rollbacks need no
migration; and cyberchef.env reaches nginx but never the served bundle.
Scaled-down installations are called out conditionally, naming whichever of
scaleDown / cyberchef.replicas caused it, via the chart's own replicas
helper so the notice cannot drift from the rendered Deployment.
The URL is only ever rendered from .Values.ingress.domain, so no domain is
stored in the chart. Refs #43.
Third change of the same class (#99): an override written without
children is YAML null and replaced the whole default sub-tree, rendering
"securityContext: null" (unhardened container), a vanished probe or a
container without resource accounting - silently, while the workload
reports healthy.
The defaultedDict helper from #96 is adopted for both workloads, so an
empty block means "chart defaults apply" while an explicitly set value
still wins, including false and 0.
The server probes rebuild their Host header from ingress.domain via the
passed-in root context, because the 2.x server rejects requests whose
Host is neither loopback nor a CORS_ORIGIN host.
The schema types allow null for exactly these blocks, so schema and
helper agree instead of the schema rejecting what the helper absorbs.
Rendering with normal values is byte-identical to the previous version,
with and without the guacd sidecar.
Two changes, both about values that do not do what they promise:
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level, so a key the chart does not read is rejected
at render time instead of being silently ignored.
2. Null-safety for hardening, probes and resources (#99): an override
written without children is YAML null and replaced the whole default
sub-tree, rendering "securityContext: null" (unhardened container), a
vanished probe or a container without resource accounting - silently.
The defaultedDict helper from #96 is adopted so an empty block means
"chart defaults apply", while an explicitly set value still wins,
including false and 0.
The schema types allow null for exactly these blocks, so the two changes
agree instead of the schema rejecting what the helper is meant to absorb.
No ingress gate here: this chart already gates its Ingress correctly.
Rendering with normal values is byte-identical to the previous version.
Major bump: a schema can break existing deployments that carry dead keys.
Two changes of the same class - values that do not do what they promise:
1. values.schema.json (#68): additionalProperties: false on every
chart-owned object level, so a key the chart does not read is rejected
at render time instead of being silently ignored. Objects passed
verbatim into the pod spec (probes, securityContext, global) stay open.
2. Ingress gate (#93): the Ingress template is wrapped in
{{- if .Values.ingress.enabled }} and the key is added to values.yaml
with default true, so behaviour is unchanged. Without the gate the
value did not exist at all and could not switch the Ingress off.
Major bump: a schema can break existing deployments that carry dead keys.
The chart mounted no volumes at all while running with
readOnlyRootFilesystem: true, so every Next.js write to
/app/apps/web/.next/cache failed. The image optimizer's failure even
surfaced as an unhandledRejection, so this was more than log noise.
The cache path is now a writable emptyDir; path and an optional
sizeLimit are configurable under rallly.cache. The hardening is
untouched — the pod's existing fsGroup (1000) is what makes the mounted
volume writable for uid 20000.
Verified in a throwaway k3d cluster which paths the app actually writes:
with a fully writable root filesystem and the cache mounted, the only
path the container touches is the cache directory itself. /tmp, $HOME
(=/app) and the rest of the root filesystem stay untouched, so no second
mount is needed.
Also drops the vestigial "capabilities.add: []": Trivy's AVD-KSV-0022 for
rallly (#84) comes from the older chart version deployed in the cluster,
which rendered a null list entry under add:. Reproduced with trivy config
against both renderings; the app needs no capabilities.
Major bump: runtime behavior changes, a volume is added.
Fixes#60
A "barcodes:" key left without children (e.g. after deleting the last
remaining sub-key, PAPERLESS_CONSUMER_BARCODE_SCANNER, which paperless-ngx
3.0 removed) is YAML null and replaced the whole default sub-tree on merge.
Rendering then failed on the nil pointer, so the release simply stopped
updating.
An empty or null block now means "the chart defaults apply". The merge runs
through a new paperless-ngx.defaultedDict helper: nulls never delete, nested
maps merge recursively, and explicitly set values win - including false and 0,
which "default" and "mergeOverwrite" would swallow. A scalar where the
defaults define a map is rejected with the values path in the message.
Major bump: the meaning of a values override changes.
Fixes#61
additionalProperties: false on every chart-owned object level, so a key
the chart does not read is rejected at render time instead of being
silently ignored. Objects passed verbatim into the pod spec (probes,
securityContext, global) stay open - Kubernetes owns their schema there.
Major bump: a schema can break existing deployments that carry dead keys.
Refs #68
additionalProperties: false on every chart-owned object level, so a key
the chart does not read is rejected at render time instead of being
silently ignored. Objects passed verbatim into the pod spec (probes,
securityContext, global) stay open - Kubernetes owns their schema there.
Major bump: a schema can break existing deployments that carry dead keys.
Refs #68
Home Assistant reads configuration.yaml, http-config.yaml and
recorder-config.yaml from a 1Password-backed Secret, mounted with subPath.
Two different gaps hide behind that, and only one of them can be closed.
Repointing secrets.configSecretRef at a different vault item used to leave the
running pod on the old configuration. That is now covered: the pod template
carries checksum/secret-ref over the rendered OnePasswordItem. The annotation
is named apart from checksum/config on purpose - it only promises that the
config source changed, not the config itself - matching the pattern
minecraft-server introduced.
Changing the CONTENT of the item in the vault still does not restart anything,
and cannot: Helm never sees the content, so there is nothing to hash. The
operator's own auto-restart annotation is no way out either - onepassword-
operator 1.12.0 enumerates exactly one workload type in its secret-update
handler (appsv1.DeploymentList) and this workload is a StatefulSet, so the
annotation would be a silent no-op advertising a guarantee it cannot keep. It
is deliberately not set. values.yaml states the limitation and the manual
rollout restart it requires; the README guideline records the operator finding,
the annotation precedence trap, and the checksum/secret-ref pattern so neither
is rediscovered the hard way.
Major bump: runtime behavior changes - changing the secret reference now costs
a restart. For this release the effect is preventive, since it currently runs
at zero replicas via scaleDown.
Last of the three publishes that carry the checksum annotations from
3.0.0+up2026.8.2.java8, leaving main on the line of the only running server.
Same chart content as the java8 and java17 publishes, only the published Java
line changes: appVersion moves to the dated tag 2026.8.2-java21 and the
minecraft.version default moves to 1.21.1, the version ftb-skies-2 actually
runs (Java 8 runs up to 1.16.5, 1.18-1.20.4 need Java 17, 1.20.5+ need
Java 21).
Against the state before this series (2.0.0+up2026.8.2.java21) the only
replaced line in Chart.yaml and values.yaml is the chart version; appVersion
and the minecraft.version default are byte-identical, everything else is
additive.
Refs #82.
additionalProperties: false on every chart-owned object level, so a key
the chart does not read is rejected at render time instead of being
silently ignored. Objects passed verbatim into the pod spec (probes,
securityContext, global) stay open - Kubernetes owns their schema there.
Major bump: a schema can break existing deployments that carry dead keys.
Refs #68
Second of the three publishes that carry the checksum annotations from
3.0.0+up2026.8.2.java8. Same chart content, only the published Java line
changes: appVersion moves to the dated tag 2026.8.2-java17 and the
minecraft.version default moves to 1.20.1, the line's matching Minecraft
version (Java 8 runs up to 1.16.5, 1.18-1.20.4 need Java 17, 1.20.5+ need
Java 21).
Refs #82.