Skip to content

Upgrade notes

Check the notes for every version between your installed version and the version you plan to deploy. Each section lists configuration changes and the steps needed to keep your existing routes working. For routine chart upgrades, see Upgrading with Helm.

Upgrading to 0.5

0.5 lets a new HAProxy pod load a previously acknowledged configuration when a restarted controller or new leader can't render. During recovery, existing configured pods keep serving without a reload. Follow the normal Helm upgrade procedure; this feature needs no new Helm values.

Establish a recovery checkpoint

Before relying on retained recovery during node maintenance, let a 0.5 controller complete a successful configuration deployment and publish its acknowledgement. That creates the retained configuration and auxiliary files in Kubernetes. An installation upgraded while rendering is already blocked has no checkpoint from the older controller. Repair the rendering error first; upgrading alone doesn't make replacement HAProxy pods Ready in that case.

The controller uses a checkpoint only when it matches the latest recorded deployment and the configured HAProxy pods. A newer deployment without a usable checkpoint prevents recovery from an older one. Retained recovery also requires at least one controller to pass its startup checks.

Account for frozen routing inputs

While rendering is blocked, replacement pods receive the retained endpoint lists, routes, certificates, and fetched files. They don't receive subsequent input changes until rendering recovers. A Ready proxy can therefore still point at a backend address that no longer exists.

The bundled PrometheusRule alerts when retained recovery is active. /healthz includes the render error but continues to report controller component health; HTTP 200 doesn't prove that current routing inputs have been applied. See controller failure behavior for recovery checks and monitoring.

Upgrading to 0.4

0.4 ships a values schema. Helm checks your values against it on every helm install, helm upgrade, helm template, and helm lint, and refuses the release if a key is unknown or a value has the wrong type. Earlier versions ignored such keys, so a values file that installs 0.3 can fail on 0.4. If you run 0.2, complete Upgrading to 0.3 first.

Confirm that the target version is available on the releases page before running the upgrade commands.

1. Save your values

Set the installed Helm release and namespace:

HAPTIC_RELEASE=haptic
HAPTIC_NAMESPACE=haptic

Export the values you supplied to Helm:

umask 077
helm get values "$HAPTIC_RELEASE" --namespace "$HAPTIC_NAMESPACE" \
  --output yaml > haptic-values-0.4.yaml
if [ "$(cat haptic-values-0.4.yaml)" = null ]; then
  printf '{}\n' > haptic-values-0.4.yaml
fi

If you manage values in Git, check that file instead, and apply the fixes from the next step to it.

2. Check your values against the schema

Render the 0.4 chart with your values. This contacts no cluster and changes nothing:

helm template "$HAPTIC_RELEASE" \
  oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --version 0.4.1 --namespace "$HAPTIC_NAMESPACE" \
  --values haptic-values-0.4.yaml > /dev/null

The command prints nothing when your values pass. Otherwise Helm names each rejected value by its path:

Error: values don't meet the specifications of the schema(s) in the following chart(s):
haptic:
- at '/controller/service': additional properties 'tpye' not allowed
- at '/haproxy/ports/http': got string, want integer

Fix each reported value, then run the command again until it passes:

  • Unknown key: correct the spelling, or delete the key. A key the chart moved or renamed fails with a message that names its replacement.
  • Wrong type: remove the quotes from a number or a boolean, for example "8080" → 8080 and "true" → true.

Free-form maps keep accepting any key: labels, annotations, selectors, resources, security contexts, affinity, and the top level of controller.config.templatingSettings.extraContext.

3. Check auth-headers-request header names

haproxy-haptic.org/auth-headers-request and haproxy-ingress.github.io/auth-headers-request no longer forward header names that contain _. List them; this needs kubectl and jq:

kubectl get ingress --all-namespaces --output json |
  jq -r '.items[] | .metadata as $m | ($m.annotations // {}) | to_entries[]
    | select(.key == "haproxy-haptic.org/auth-headers-request"
        or .key == "haproxy-ingress.github.io/auth-headers-request")
    | select(.value | contains("_"))
    | "\($m.namespace)/\($m.name) \(.key): \(.value)"'

Replace _ with - in each listed header name, and have clients send the dashed header. The auth service needs no change: HAPTIC already delivered these headers to it with - in their names.

If you don't: the header isn't forwarded to the auth service, the Ingress gets an InvalidAuthHeader Warning Event, and the admission webhook denies new or changed Ingresses that list one.

4. Review Gateway route timeouts

HAPTIC now ignores rules[].timeouts.request and uses only backendRequest for HAProxy's server inactivity timeout, matching HAProxy Unified Gateway's field mapping. Previously, request took precedence over backendRequest.

If you used request: 30s to set the server timeout, move that value to backendRequest in the route rule:

timeouts:
  backendRequest: 30s

If both fields are set, the existing backendRequest value now applies. If backendRequest is absent, the backend default applies. A zero backendRequest now uses HAProxy's maximum timeout (about 24.9 days) instead of the backend default.

These settings limit inactivity. HAPTIC doesn't enforce an overall request deadline across retries; see Timeout limits.

5. Monitor rejected resource changes

Alert when haptic_rejected_watched_inputs is greater than zero. HAPTIC now keeps an invalid resource change out of otherwise valid updates, so healthy traffic doesn't prove every intended change took effect. A rejected credential rotation or policy update retains the previous validated behavior. See Rejected resource changes for diagnosis and restart behavior.

6. Upgrade the release

Pass the checked values file. --reset-values starts from the new chart's defaults, so the release doesn't carry rejected keys forward:

helm upgrade "$HAPTIC_RELEASE" \
  oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --namespace "$HAPTIC_NAMESPACE" --version 0.4.1 \
  --reset-values \
  --values haptic-values-0.4.yaml

Upgrading to 0.3

Use this checklist to upgrade a 0.2 release. Work through it from top to bottom; each step says what to change, and what fails if you don't. If you run 0.1.0 or a 0.2.0 alpha, complete Upgrading to 0.2 first.

Confirm that the target version is available on the releases page before running the upgrade commands. Development documentation can describe a release before its artifacts are published.

1. Save your values

Set the installed Helm release and namespace:

HAPTIC_RELEASE=haptic
HAPTIC_NAMESPACE=haptic

Export the values you supplied to Helm, and copy them into a working file for the upgrade:

umask 077
helm get values "$HAPTIC_RELEASE" --namespace "$HAPTIC_NAMESPACE" \
  --output yaml > haptic-values-before.yaml
cp haptic-values-before.yaml haptic-values-0.3.yaml

If the export contains only null, replace it with an empty map:

if [ "$(cat haptic-values-0.3.yaml)" = null ]; then
  printf '{}\n' > haptic-values-0.3.yaml
fi

Apply the following steps to haptic-values-0.3.yaml. If you manage values in Git, apply them to that source too.

2. Rename the timeout and SSL redirect keys

Under controller.config.templatingSettings.extraContext, rename these keys:

0.2 key 0.3 key
timeout_connect timeoutConnect
timeout_client timeoutClient
timeout_server timeoutServer
timeout_http_request timeoutHttpRequest
timeout_http_keep_alive timeoutHttpKeepAlive
ssl_redirect_default sslRedirectDefault

sslRedirectDefault is a boolean. Write true, not "true":

controller:
  config:
    templatingSettings:
      extraContext:
        timeoutConnect: "5000"
        sslRedirectDefault: true

If you don't: Helm refuses to install or upgrade the chart, for example:

controller.config.templatingSettings.extraContext.timeout_connect was renamed in 0.3.0 and no longer has any effect. Rename it to timeoutConnect.

A quoted sslRedirectDefault: "true" fails the same way, because a string would never turn the redirect on.

3. Move the HSTS max-age

extraContext.hapticHstsMaxAge is removed. haproxy-haptic.org/hsts without haproxy-haptic.org/hsts-max-age now sends max-age from tls.hsts.maxAge, which defaults to one year (31536000) instead of two.

If you set hapticHstsMaxAge, or you want to keep the two-year default, set tls.hsts.maxAge:

controller:
  config:
    templatingSettings:
      extraContext:
        tls:
          hsts:
            maxAge: "63072000"

tls.hsts.maxAge also sets the max-age of the global HSTS header when tls.hsts.enabled is true.

If you don't: a leftover hapticHstsMaxAge fails the install with the same "was renamed in 0.3.0" error as step 2. Without it, Ingresses using haproxy-haptic.org/hsts send max-age=31536000.

4. Replace basic-auth hashes HAPTIC now refuses

Every auth-secret annotation (haproxy-haptic.org, haproxy.org, haproxy-ingress.github.io, and nginx.ingress.kubernetes.io) accepts only bcrypt, SHA-256 crypt, SHA-512 crypt, and yescrypt hashes by default. It refuses $apr1$ (Apache MD5), {SHA}, $1$ (MD5-crypt), Data Encryption Standard (DES) crypt, and plaintext credentials.

Preflight validation doesn't read your cluster's Secrets, so check them before upgrading. This lists every user whose hash the 0.3 default refuses; it needs kubectl and jq:

pattern='^\$(2[aby]|5|6|y)\$[./0-9A-Za-z$=]+$'
kubectl get ingress --all-namespaces --output json |
  jq -r '.items[] | .metadata as $m | ($m.annotations // {}) | to_entries[]
    | select(.key | endswith("/auth-secret"))
    | (if (.value | contains("/")) then .value else "\($m.namespace)/\(.value)" end)
      + " " + $m.namespace + "/" + $m.name' |
  sort -u |
  while read -r secret ingress; do
    kubectl get secret --namespace "${secret%%/*}" "${secret#*/}" --output json |
      jq -r --arg secret "$secret" --arg ingress "$ingress" --arg pattern "$pattern" '
        .data
        | if has("auth") then
            .auth | @base64d | split("\n")[] | rtrimstr("\r")
            | select(contains(":")) | capture("^(?<user>[^:]*):(?<hash>.*)$")
          else
            to_entries[] | {user: .key, hash: (.value | @base64d)}
          end
        | select(.hash | test($pattern) | not)
        | "Ingress \($ingress): Secret \($secret), user \(.user)"'
  done

For each user listed, generate a new hash and write it into the Secret in the format the Secret already uses. Each command prompts for the password:

Format Command
bcrypt htpasswd -nB <user>
SHA-512 crypt openssl passwd -6
SHA-256 crypt openssl passwd -5
yescrypt mkpasswd -m yescrypt

If you don't: rendering fails with an error that names the Ingress, the Secret, and the user. The admission webhook denies new or changed Ingresses that reference such a Secret, and while one is in the cluster, HAPTIC applies no configuration changes. To keep accepting a format while you migrate, widen the patterns as shown in Basic-auth password hashes.

5. Check host alias annotations

haproxy-haptic.org/host-alias-regex and haproxy-ingress.github.io/server-alias-regex must now match the whole hostname; in 0.2 they matched any part of it. List every regex alias; this needs kubectl and jq:

kubectl get ingress --all-namespaces --output json |
  jq -r '.items[] | .metadata as $m | ($m.annotations // {}) | to_entries[]
    | select(.key == "haproxy-haptic.org/host-alias-regex"
        or .key == "haproxy-ingress.github.io/server-alias-regex")
    | "\($m.namespace)/\($m.name) \(.key): \(.value)"'

For each regex that should match only part of a hostname, widen it to cover the rest. For example, example\.com becomes .*example\.com, and example becomes .*example.*. A regex that already starts with ^ and ends with $ needs no change. HAPTIC also rejects a regex whose groups or [ ] classes don't close within it.

Exact aliases (haproxy-haptic.org/host-alias, haproxy-ingress.github.io/server-alias, and nginx.ingress.kubernetes.io/server-alias) must now be valid hostnames, optionally starting with *.; case doesn't matter. List the aliases HAPTIC now refuses:

kubectl get ingress --all-namespaces --output json |
  jq -r '.items[] | .metadata as $m | ($m.annotations // {}) | to_entries[]
    | select(.key == "haproxy-haptic.org/host-alias"
        or .key == "haproxy-ingress.github.io/server-alias"
        or .key == "nginx.ingress.kubernetes.io/server-alias")
    | .key as $key
    | .value | split(if $key == "haproxy-haptic.org/host-alias" then "[, ]" else "," end; null)[]
    | gsub("^\\s+|\\s+$"; "") | select(. != "")
    | select(test("^(\\*\\.)?[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*$"; "i") | not)
    | "\($m.namespace)/\($m.name) \($key): \(.)"'

Remove or correct each listed alias. A port, a path, or an underscore can't be part of an alias.

If you don't: a regex that relied on matching part of a hostname stops matching, and requests for those hostnames no longer reach the Ingress. An invalid alias or regex is skipped with an InvalidHostAlias, InvalidServerAlias, InvalidServerAliasRegex, InvalidHostAliasRegex or InvalidAnnotationValue Warning Event, and the admission webhook denies new or changed Ingresses that carry one.

6. Update custom templates

If your values add templates, snippets, or libraries, make these changes:

0.2 0.3
http.Fetch option delay interval
strings_replace(s, old, new) replace(s, old, new)
trim(s) to strip whitespace strip(s)

trim is now the trim(s, cutset) builtin in every render path. To find candidates in your values file:

grep -nE 'strings_replace|trim\(|delay' haptic-values-0.3.yaml

If you don't: the configuration fails to load, and preflight reports the error. An http.Fetch call that sets delay fails with option "delay" was removed, so this call fails. Rename it to "interval".

7. Update custom validation tests

Assertions in validationTests no longer see your templatingSettings.extraContext. They render with the libraries' defaults, testExtraContext (which the chart sets from its default values), _global's extraContext, and the test's own extraContext. A test that asserts output of a value you set must set that value itself:

controller:
  config:
    validationTests:
      test-my-timeouts:
        extraContext:
          timeoutConnect: "5000"
        # fixtures and assertions unchanged

Each test also renders its fixtures a second time with your extraContext. That render must succeed and, where the test asserts haproxy_valid, pass haproxy -c. See Extra context.

If you don't: a test asserting one of your values fails, and so does a test whose fixtures your values break. Either failure stops the configuration from loading, and preflight reports it, for example This test's fixtures fail to render with the deployment's extraContext.

8. Prepare for HTTP/3

0.3 enables HTTP/3 by default. Every TLS-terminating HTTPS listener also listens on UDP, and HTTPS responses advertise it with an alt-svc header. The HAProxy Service gains a UDP port named https-quic with the https port's number and nodePort, and each Gateway's Service gains a UDP port for each TLS-terminating HTTPS listener. The chart's NetworkPolicy allows it.

Before upgrading, choose one:

  • Allow UDP on the HTTPS port (443 by default), or on its nodePort, through firewalls, security groups, and load balancers in front of HAProxy. A LoadBalancer Service now mixes TCP and UDP ports, so check that your load-balancer implementation supports that.
  • Keep HAProxy TCP-only:

    controller:
      config:
        templatingSettings:
          extraContext:
            http3:
              enabled: false
    

If your load balancer publishes UDP on a different port than TCP, set http3.altSvc.port; see HTTP/3 (QUIC).

If you don't: where UDP is blocked, clients fall back to HTTP/2 or HTTP/1.1 over TCP after trying QUIC. An entry named https-quic in haproxy.service.extraPorts fails the install; rename it or turn HTTP/3 off.

9. Check deployment-specific settings

Skip each item that doesn't apply to you.

Varnish cache (cache.varnish.enabled: true). Varnish runs as a bundled non-root image that needs the IPC_LOCK capability. Kubernetes Baseline and Restricted Pod Security Standards reject it, so arrange a policy exception with your cluster administrator before upgrading. If you set cache.varnish.image, remove the override to use the bundled image, or supply an image with cap_ipc_lock=ep on varnishd. If you don't: your cluster's policy rejects the Varnish pods. See shared-log memory.

Controller probe overrides. The chart probes controller readiness on /readyz and liveness on /livez. If your values set controller.readinessProbe.httpGet.path or controller.livenessProbe.httpGet.path, change them:

controller:
  readinessProbe:
    httpGet:
      path: /readyz
  livenessProbe:
    httpGet:
      path: /livez

If you don't: probes on /healthz mark the leading replica unready and restart it while a new configuration fails to load, so admission requests are denied.

Your own controller NetworkPolicy. If you replace the chart's NetworkPolicy, allow controller pods to reach each other on the health port (controller.ports.healthz, default 8080).

Monitoring. haptic_events_dropped_total is removed. Query haptic_events_dropped_critical_total, which counted the same drops.

Custom agent clients. If your own code calls the agent's /v1/apply, set identity_version: 1 in the manifest. If you don't: the agent rejects the manifest with 400. HAPTIC's own controller already sets it.

10. Validate the candidate

Use the haptic binary matching the chart version to run preflight validation:

helm pull oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --version 0.3.0 --untar --untardir ./haptic-0.3-chart
haptic preflight --values ./haptic-values-0.3.yaml \
  --chart ./haptic-0.3-chart/haptic --expect-chart-version 0.3.0 \
  --namespace "$HAPTIC_NAMESPACE" --release "$HAPTIC_RELEASE"

Fix every reported error and run it again until it passes.

0.3 adds the testExtraContext field to the HAProxyTemplateConfig and HAProxyTemplateLibrary CRDs. Keep the chart's CRD upgrade hook enabled. If you manage CRDs separately, apply the target chart's schemas before upgrading:

helm show crds oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --version 0.3.0 | kubectl apply --server-side --force-conflicts -f -

11. Upgrade the release

Pass the complete migrated values file. --reset-values starts from the new chart's defaults, so removed keys aren't carried forward from the installed release:

helm upgrade "$HAPTIC_RELEASE" \
  oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --namespace "$HAPTIC_NAMESPACE" --version 0.3.0 \
  --reset-values \
  --values haptic-values-0.3.yaml

If validation rejects the candidate, fix the reported value or template and repeat the upgrade. For rollout failures, see Recover a failed upgrade.

12. Verify the deployment

Wait for the controller and HAProxy deployments:

kubectl --namespace "$HAPTIC_NAMESPACE" get deployments \
  --selector "app.kubernetes.io/instance=$HAPTIC_RELEASE" --output name | \
while read -r deployment; do
  kubectl --namespace "$HAPTIC_NAMESPACE" rollout status "$deployment" --timeout=7m
done

The HAProxyTemplateConfig should report Validated=True; check any False condition on HAProxyCfg:

kubectl --namespace "$HAPTIC_NAMESPACE" get haproxytemplateconfig,haproxycfg -o yaml

Test existing HTTP and HTTPS routes, including authentication and custom annotations.

Behavior changes to review

These need no action unless you depend on the old behavior:

  • Response headers on generated responses. The chart sets Server, HSTS, Ingress custom response headers, and routing diagnostic headers with http-after-response, so error pages, denials, and redirects that HAProxy generates or replaces carry them too. A snippet that changes one of these headers with http-response runs before the chart's rule; use http-after-response instead.
  • HSTS default. haproxy-haptic.org/hsts without hsts-max-age sends one year instead of two; see step 3.
  • nginx-ingress regex paths. With use-regex or rewrite-target, a path containing regex syntax matches as a case-insensitive regex anchored at the path's start, and $N in rewrite-target refers to its capture groups. In 0.2 the path matched as a literal prefix. See use-regex.
  • Host alias conflicts. An exact alias for a hostname that an older Ingress already claims, as a rule host or alias, is no longer routed, and the newer Ingress gets a RouteConflict Warning Event. See Host aliases claim hostnames.
  • A failing configuration change. While a new configuration fails to load, the leading controller replica keeps serving and validating the previous one, so admission keeps working. Other replicas report unready and restart. /healthz still reports the failure. See health checks.

Upgrading to 0.2

Use this guide to upgrade from 0.1.0 or a 0.2.0 alpha to a stable 0.2 release. The controller and chart share one version. The chart replaces the HAProxy Data Plane API with the HAPTIC agent and changes several values paths.

Confirm that the target version is available on the releases page before running the upgrade commands. Development documentation can describe a release before its artifacts are published.

Check requirements

  • Kubernetes 1.33 or newer. The HAProxy pod uses native sidecars to keep its agent, Stream Processing Offload Agent (SPOA) hub, and log collector running until HAProxy exits.
  • Capacity for the whole installation and its rolling upgrade. The defaults request about 5.4 GiB of memory, rising to 8.1 GiB during rollout. The pre-rollout validation Job has a separate 1 GiB limit. Use the resource sizing guidance for larger installations or customized pod resources.
  • Access to the controller registry from HAProxy pods: their agent now uses the HAPTIC image. Set image-pull credentials if your cluster requires them.

The chart supports HAProxy 3.0–3.4. Keep your selected haproxyVersion unless you intend to change it; the default is 3.4. See HAProxy versions for runtime-update coverage.

Save your values

Set the installed Helm release and namespace, and the 0.2 release to upgrade to:

HAPTIC_RELEASE=haptic
HAPTIC_NAMESPACE=haptic
HAPTIC_VERSION=0.2.2

Export the values you supplied to Helm:

umask 077
helm get values "$HAPTIC_RELEASE" --namespace "$HAPTIC_NAMESPACE" \
  --output yaml > haptic-values-before.yaml

Copy the exported values, which include settings supplied with --set, into a working file for the upgrade:

cp haptic-values-before.yaml haptic-values-0.2.yaml

If the export contains only null, replace it with an empty map:

if [ "$(cat haptic-values-0.2.yaml)" = null ]; then
  printf '{}\n' > haptic-values-0.2.yaml
fi

Keep haptic-values-before.yaml as the record of your previous configuration. If you manage values in Git, also apply the migrations below to that source so subsequent deployments keep them.

Migrate 0.1.0 values

For an alpha installation, some migrations may already be applied. Check the values you use against the table and review the changed defaults before validation.

For 0.1.0, update the paths you use in haptic-values-0.2.yaml:

Previous value Replacement
Root controller workload settings, such as image, replicaCount, resources, webhook, monitoring, networkPolicy, probes, and autoscaling The same setting under controller.*
Root pod settings, such as imagePullSecrets, nodeSelector, tolerations, affinity, podAnnotations, and podSecurityContext The same setting under controller.podSpec.*
HAProxy pod settings, such as haproxy.nodeSelector, .tolerations, .affinity, .podAnnotations, and .podSecurityContext The same setting under haproxy.podSpec.*
controller.crdName controller.configName
controller.debugPort controller.ports.healthz
controller.defaultSSLCertificate defaultSSLCertificate
controller.config.dataplane.port haproxy.ports.dataplane
controller.config.routing.regexMatchOrder controller.config.templatingSettings.extraContext.routing.regexMatchOrder
controller.templateLibraries.pathRegexLast.enabled: true controller.config.templatingSettings.extraContext.routing.regexMatchOrder: last
haproxy.dataplane.logLevel, .resources, .extraEnv, and .service The same setting under haproxy.agent.*

For example, controller replicas and a custom default certificate now use:

controller:
  replicaCount: 2
defaultSSLCertificate:
  secretName: my-default-certificate
  certManager:
    enabled: false

Remove Data Plane API-only values (haproxy.dataplane.validateConfig, debugSocketPath, aclFormat, and haproxy.dataplaneBin), haproxy.enterprise.version, and controller.config.templatingSettings.extraContext.serverSlots. haproxyVersion selects the Enterprise revision when Enterprise is enabled.

Remove spoaHub.plugins.otel. Configure tracing through the tracing values instead.

The chart rejects legacy paths with their replacements. Run the validation step below to find remaining paths, including renamed extraContext settings.

Review changed defaults and integrations

The default IngressClass and GatewayClass names change from haproxy to haptic. To preserve routes that use the 0.1.0 defaults, add these values:

ingressClass:
  name: haproxy
gatewayClass:
  name: haproxy

Keep your existing class names if you already customized them. Alternatively, update the class references on your Ingress and Gateway resources to haptic.

Enable each vendor annotation library that your existing routes use. For example, to retain support for ingress-nginx annotations:

controller:
  templateLibraries:
    nginxIngress:
      enabled: true

Use haproxyIngress.enabled for haproxy-ingress.github.io/* and haproxytech.enabled for haproxy.org/* under the same templateLibraries map. The native haproxy-haptic.org/* library remains enabled by default.

Review these behavior changes before upgrading:

  • Access logs use JSON. Adapt custom log parsers or override the log-format snippets; see access logging.
  • Ingress serves HTTPS using the default certificate. Set controller.config.templatingSettings.extraContext.ingressDefaultHTTPS: false if you require plaintext-only Ingress without an explicit TLS declaration.
  • HAProxy response compression is opt-in. If you used an alpha version's automatic compression, set haproxy-haptic.org/compress-enable: "true" on appropriate routes. Avoid compressing responses that combine secrets with attacker-controlled input; see compression settings.
  • Hash-based balancing uses consistent hashing. Existing key distribution can change during the upgrade.
  • Request retries that replay a request apply to idempotent methods by default. Review custom retry settings before restoring retries for other methods.
  • The backend connection timeout defaults to 100 ms instead of 5 s. For slower networks, set controller.config.templatingSettings.extraContext.timeout_connect to "5000" to retain the previous timeout in milliseconds.
  • HAProxy's HTTP/HTTPS container ports default to 80/443 instead of 8080/8443. Update integrations that address pod ports directly, or retain the previous ports through haproxy.ports.http and haproxy.ports.https.

The webhook uses a chart-managed self-signed certificate by default. To retain cert-manager renewal, set controller.webhook.certManager.enabled: true and keep cert-manager installed. The chart uses a new default webhook Secret name to avoid conflicting with the old cert-manager-owned Secret.

Update integrations that invoke haptic-controller to invoke haptic. Replace Data Plane API integrations with the agent interface and update dashboards using the metric migration table.

If you maintain custom templates, replace reads of parsed currentConfig sections with currentConfig.ServerIndex for previous servers, and use currentServers in validation fixtures. Use toJSON instead of implicit string conversion for maps and slices. Pass the watched resource object to statusPatch(resource, variants) instead of separate namespace, name, API version, and kind arguments.

If you process rendered manifests, accept one HAProxyTemplateConfig and its referenced HAProxyTemplateLibrary objects. haptic config view --input merges them for inspection. Helm owns these objects; put persistent overrides in your values file.

Validate the candidate

Use the haptic binary matching the chart version below to run preflight validation:

helm pull oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --version "$HAPTIC_VERSION" --untar --untardir ./haptic-0.2-chart
haptic preflight --values ./haptic-values-0.2.yaml \
  --chart ./haptic-0.2-chart/haptic --expect-chart-version "$HAPTIC_VERSION" \
  --namespace "$HAPTIC_NAMESPACE" --release "$HAPTIC_RELEASE"

Keep the default CRD upgrade and pre-rollout validation hooks enabled. If your deployment manages CRDs separately, apply the target chart's schemas before upgrading:

helm show crds oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --version "$HAPTIC_VERSION" | kubectl apply --server-side --force-conflicts -f -

GitOps diff tools can need these CRDs before they can map the new library resources, because diff runs before Helm's hooks.

Upgrade the release

Pass the complete migrated values file. --reset-values starts from the new chart's defaults before applying that file, so removed legacy settings aren't carried forward from the installed release:

helm upgrade "$HAPTIC_RELEASE" \
  oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
  --namespace "$HAPTIC_NAMESPACE" --version "$HAPTIC_VERSION" \
  --reset-values \
  --values haptic-values-0.2.yaml

If validation rejects the candidate, fix the reported value or template and repeat the upgrade. Keep validation enabled. For rollout failures and rollback limitations, see Recover a failed upgrade.

Verify the deployment

Wait for both controller and HAProxy deployments:

kubectl --namespace "$HAPTIC_NAMESPACE" get deployments \
  --selector "app.kubernetes.io/instance=$HAPTIC_RELEASE" --output name | \
while read -r deployment; do
  kubectl --namespace "$HAPTIC_NAMESPACE" rollout status "$deployment" --timeout=7m
done

Inspect configuration validation and per-pod deployment status. The HAProxyTemplateConfig should report Validated=True; check any False condition on HAProxyCfg for a rejected configuration or failed deployment:

kubectl --namespace "$HAPTIC_NAMESPACE" get haproxytemplateconfig,haproxycfg -o yaml

Test existing HTTP and HTTPS routes, including authentication and custom annotations. A successful Helm command alone doesn't prove traffic has converged. See troubleshooting if a pod or route remains unavailable.

Found a problem on this page? Report it