Repository navigation
v0.6.0
[0.6.0] - 2026-10-18
HAPTIC 0.6 moves to GitHub, requires Kubernetes 1.35 or newer, and drops
HAProxy 3.1. It ships security updates for Go, HAProxy, and the bundled Coraza
WAF plugin, and makes retained recovery hold up through ordinary leader changes
and critical http.Fetch outages.
Before upgrading: check your Kubernetes version, move off HAProxy 3.1, and
remove any registry.gitlab.com image overrides. Read the
upgrade notes.
Added
- Metrics:
haptic_render_errors_total: failed reconcile renders on every replica, by cache state.haptic_haproxy_checks_totalandhaptic_haproxy_checks_reused_total:haproxy -cruns per validator and result on every replica, and the runs answered by an identical input's verdict instead.haptic_admission_render_slot_wait_seconds: how long admission dry runs queue for a render slot.
- Select several validation tests in one run by repeating
haptic validate --test. MUTEX_PROFILE_FRACTIONandBLOCK_PROFILE_RATEenable the mutex and block profiles on/debug/pprof.
Changed
- BREAKING: The Go module path is now
github.com/haproxy-haptic/HAPTIC(wasgitlab.com/haproxy-haptic/haptic). Programs that import HAPTIC packages must update their import paths. - Development, issues, releases, and container images moved to GitHub: github.com/haproxy-haptic/HAPTIC and
ghcr.io/haproxy-haptic. Releases up to 0.5.0 are available there with their original digests and signatures. - Reconciliation CPU and latency no longer grow with the size of the watched set: input isolation inspects only the resources changed since its last capture.
- The leader runs
haproxy -conce per render instead of twice: the render gate reuses the verdict input acceptance just gave the identical config and files. - Recovery checkpoints share unchanged auxiliary files instead of copying every map, file, and certificate Secret, and publications read auxiliary files with one list per kind. Steady deployment churn no longer creates a full object set per checkpoint, and most of the controller's API requests are gone.
- Drift prevention no longer re-applies every HAProxy pod's unchanged status to the
HAProxyCfgand each auxiliary file every interval: it reads them live and corrects only entries that changed. On an idle fleet this removes about two thirds of the controller's API requests. - Startup validation may use 80% of the controller's startup probe window (240 seconds by default) instead of a fixed 120 seconds, so a large validation-test suite on a small node no longer crash-loops the controller.
- Failed renders count in
haptic_render_total, and failed reconciliations inhaptic_reconciliation_duration_seconds. A follower warns when its renders start failing. haptic_webhook_request_duration_secondsbuckets extend to 30 seconds, so latencies near the admission timeout are measurable.- Validation test totals and results are logged at INFO.
- Fewer allocations during input isolation, validation, and rendering.
Fixed
- Retained recovery:
- A leader that stops gracefully finishes its deployment in progress, including a pending HAProxy reload, and saves its recovery checkpoint before it releases the Lease. Previously, after an ordinary leader change, replacement HAProxy pods could stay NotReady with
RetainedConfigUnavailablewhile rendering was blocked. - A verified
/v1/stateread that overlapped an apply could restore the dataplane agent's previous file set, so the pod reporteddisk_is_the_desired_setuntil it restarted and new pods could not recover from the checkpoint. - Recovery starts after leader failover when a critical HTTP source is unavailable, including with large watched sets.
- Pruned checkpoints are recreated when their configuration is deployed again, and retained configurations no longer exceed the storage limit once recovery plans are included.
- A leader that stops gracefully finishes its deployment in progress, including a pending HAProxy reload, and saves its recovery checkpoint before it releases the Lease. Previously, after an ordinary leader change, replacement HAProxy pods could stay NotReady with
- Critical
http.Fetchoutages: a replica without the source's content no longer renders, or re-reads every watched resource, on every change. It refetches the source everyinterval(30 seconds by default) and renders when the source answers. - A standby replica that became leader after more than 1.5 ×
driftPreventionIntervalfailed liveness until it deployed, so the kubelet restarted it, for example while a criticalhttp.Fetchsource was down. - An idle drift-prevention round no longer resends and rewrites every file on every HAProxy pod: the dataplane agent's verified read keeps a file's proof while the file still has the bytes, mode, and owner the agent wrote.
- Valid changes are no longer denied with a timeout during heavy resource churn: a CPU stays free for each admission render.
- A schema fetch timeout no longer rejects a valid HAProxyTemplateConfig change for good: it shows
Validated=Unknown(ValidationInconclusive) and is validated again with backoff. Schema reads fetch only the watched CRDs. - A configuration that fails
haproxy -cor rendering independently of watched resources is reported after two checks at startup instead of one per watched object. - A cold render cancelled while a template published a shared value no longer deadlocks the replica's first render, which left admission denied.
- Shared rate limiting no longer falls back to per-pod budgets for 5 seconds after one slow store reply (rate-limit plugin v0.4.2).
- The dataplane agent no longer reports ready before its admin and drain sockets exist, and no longer stalls 30 seconds when HAProxy is still starting its worker.
/livezand/readyzsay why a component is unhealthy.- Auxiliary-file
deployedToPodsentries name only the pods that run each file. A pod's entry no longer goes missing until the next drift check when its status lands before or while its configuration is published, and no longer lands on the previous configuration's files. - Queued events no longer delay controller shutdown.
- The playground escapes quotes in resource, annotation, and template names, so a shared link cannot inject HTML attributes.
- A new leader deploys a critical
http.Fetchsource's first content as soon as its replica accepts it. Previously, if the content was accepted just after the leader's first render, HAProxy pods stayed NotReady until the next drift-prevention round (60 seconds by default), for example right after the controller started.
Security
- Go 1.27.2 and its HTTP/2 dependency fix HTTP, TLS, and filesystem vulnerabilities.
- HAProxy 3.0.30, 3.2.26, 3.3.17, and 3.4.7 fix HTTP/2 PING frame handling and an over-long HTTP/1 chunk-size line, both marked major upstream.
- The bundled Coraza SPOA plugin v0.10.1 is built against Coraza v3.8.1 and fixes WAF bypass and crash vulnerabilities (GHSA-6gcq-wc29-5xf2, GHSA-6r3q-mjv7-xr8m, and ten Medium advisories). It also stops a memory leak when the validator rechecks an invalid WAF policy.
Helm chart
Added
- Opt-in Pod readiness gate
haproxy-haptic.org/load-balancer-ready(controller.templateLibraries.kubernetesBackends.podReadinessGate.enabled): a gated pod becomes Ready only once every HAProxy pod routes to it, so single-replica rollouts drop no requests.
Changed
- BREAKING: The chart requires Kubernetes 1.35 or newer; 1.33 is past its upstream end of life and 1.34 reaches it on 2026-10-27. Check your cluster version before upgrading.
- BREAKING: HAProxy 3.1 is no longer supported: it is past its upstream end of life and misses the 2026-10-14 HTTP/1 and HTTP/2 fixes. Helm rejects
haproxyVersion: "3.1"; switch to 3.2 or 3.4 before upgrading. - Default controller, SPOA hub, and Varnish images come from
ghcr.io/haproxy-haptic/haptic, and the chart is published atoci://ghcr.io/haproxy-haptic/haptic/charts/haptic; itshomeandsourceslinks point to GitHub. - Render the Helm chart faster by avoiding repeated YAML serialization of template libraries.
- Default controller, SPOA hub, and Varnish images come from
ghcr.io/haproxy-haptic/haptic, and the chart is published atoci://ghcr.io/haproxy-haptic/haptic/charts/haptic. - The chart renders faster.
Fixed
- The controller NetworkPolicy accepts webhook calls from any source by default. Its
0.0.0.0/0and::/0rules don't match node traffic on Cilium, so admission calls from other nodes timed out.controller.networkPolicy.ingress.webhook.fromstill restricts the webhook port when set. - With several releases in one cluster, each release's Ingress and Gateway status listed every release's load balancer addresses, so external-dns published names with other releases' IPs. Each controller now watches only its own release's load balancer Services.
- TLS Gateways use isolated service addresses and pod ports, so they can't route to another Gateway's TLSRoutes.
- Gateway API: TLSRoutes keep their hostname specificity after listener intersection, with older routes winning ties.
- Pre-rollout validation and the load gate no longer fail with
vector.enabled=false,vector.requestMetricsfeatures off,spoaHub.enabled=false, or the mirror plugin off. - haproxy-ingress: an Ingress with
haproxy-ingress.github.io/waforwaf-modefails the render whenspoaHub.enabled=falseinstead of being served without WAF inspection. - nginx-ingress:
nginx.ingress.kubernetes.io/enable-modsecurity: "true"sends the route to the Coraza WAF in detect mode, like ingress-nginx's default; it was ignored.