This patch release tightens the Helm chart's defaults for high-availability and multi-tenant Mercure deployments. New opt-in NetworkPolicy and CiliumNetworkPolicy templates keep tenants and infrastructure cleanly separated, readOnlyRootFilesystem: true plus rootless runs work without changing your securityContext, and the chart now lands in line with the restricted PodSecurity Standard out of the box. Also caps the topic-selector cache to 100k entries by default to bound memory usage on busy hubs, and fixes the broken Docker image release that held back v0.23.3 (so this is the v0.23 line you actually want to upgrade to).
✨ New Features
- Helm: Add opt-in
NetworkPolicyandCiliumNetworkPolicytemplates. Restrict ingress/egress per-tenant without templating policies outside the chart. The Cilium variant supports FQDN-pinned egress and L7 rules. Disabled by default; enable vianetworkPolicy.enabled/ciliumNetworkPolicy.enabledand supply rule lists by @dunglas in #1229 - Helm: Support
readOnlyRootFilesystem: trueout of the box./configand/tmpnow mount asemptyDirunconditionally. With the default BoltDB transport, setpersistence.enabled: trueso/datais a writable PVC. Other transports work without persistence as long as no Caddy module writes under/data(see the known-issues note above forrate_limit). Also fixesbolt.NewBoltTransporttoMkdirAllthe parent directory so a fresh empty/datadoes not crash the hub on first write by @dunglas in #1226 - Helm: Tighten secure-by-default settings.
serviceAccount.automount: false(Mercure does not call the K8s API),enableServiceLinks: falseon the hub Pod (no neighbour-Service env leak in shared namespaces),podSecurityContext.seccompProfile.type: RuntimeDefault, and a fully hardenedhelm testpod sohelm testworks on PSS-restricted clusters by @dunglas in #1231 - Helm: Pass through HTTPRoute rule timeouts. Supply
timeouts:blocks per rule when you need to bound a specific path (for instance, cap publish POSTs so a slow publisher cannot hold a gateway connection open). The chart's auto-default rule keepstimeouts.request: 0sso SSE subscribers are not cut by the gateway by @dunglas in #1223 - Helm: Expose HPA
customMetricsandbehavior. Append Pods/Object/External metrics tospec.metricsand configure scaling behavior (for instance,scaleDownpolicies tuned for SSE workloads) by @dunglas in #1217 - Helm: Seed replicas at
minReplicason fresh install with HPA. Avoids the 30 to 90 second under-provisioning window where Kubernetes defaultedspec.replicas: 1before the HPA caught up by @dunglas in 8ea6a35 - Docker: Re-apply
cap_net_bind_serviceand ship a transport-awareHEALTHCHECK. Letsdocker run --user 1000bind 80/443 without losing the file capability the upstreamcaddy:2-alpinebinary ships with. The newHEALTHCHECKhits/mercure/health/readyon the admin API instead of the deprecated/healthzby @dunglas in #1222. (Note: thesetcapline is removed in v0.23.5 because modern container runtimes setip_unprivileged_port_start=0, and the file capability conflicted with restrictive K8ssecurityContexts — see the known-issues note above.) - Examples: Harden the chat demo chart and refresh dependencies. Moves to
python:3.13-slim+gunicorn(replacing the unmaintainedtiangolo/meinheld-gunicorn), bumps Flask to 3.0, PyJWT to 2.10, and uritemplate to 4.1, ships aNetworkPolicytemplate, runs the pod non-root on a read-only rootfs, and adds an HTTPRoute alongside the Ingress by @dunglas in #1227, #1228
🐛 Bug Fixes
- Cache: Cap the default topic-selector cache to 100k entries. The previous default (2.56M) could reach ~256MB at ~100B per entry on busy hubs, putting Go's runtime in a
gcBgMarkWorkerthrashing loop nearGOMEMLIMIT. Resize per workload viatopic_selector_cachein the Caddyfile (set to-1to disable entirely) by @dunglas in 8b24ffd - CI: Restore the multi-arch Docker image build.
v0.23.3's release failed atRUN setcapbecause thelinux/arm64image build was not getting QEMU registered on the amd64 runner. Heads-up:v0.23.3is an orphan tag; upgrade directly fromv0.23.2tov0.23.4by @dunglas in #1232
📖 Documentation
- Helm: Correct the rootless deployment example. Binding 80/443 from a
securityContextwithallowPrivilegeEscalation: falserequires the binary to bind on an unprivileged port (or on a runtime withip_unprivileged_port_start=0, which is the default on containerd 1.5+ and Docker 20.10+). Recommendservice.targetPort: 8080as a portable workaround by @dunglas in #1230
The hardening defaults in this release matter most for HA and multi-tenant Mercure deployments, where pod-to-pod isolation, a restricted PodSecurity profile, and conservative cache bounds are not optional. Mercure Cloud tenants already run with every default in this release applied for them, because we manage the cluster on their behalf, alongside the production transports (Redis, Kafka, Pulsar, Postgres) and an SLA-backed managed offering. Mercure Enterprise brings the same hardening on-premise plus the HA transports and priority support. Contact contact@mercure.rocks for the managed cloud offering, on-premise licenses, custom development, consulting, and training.
Full Changelog: v0.23.2...v0.23.4