HAProxy config,
rendered from your templates.
HAPTIC — the HAProxy Template Ingress Controller — is an open-source Kubernetes controller that builds your HAProxy config from templates you control. Watch Ingress, the Gateway API, or any custom resource, and generate configuration from their fields.
{% for _, ingress := range resources.ingresses.List() %} backend {{ ingress.metadata.name }} balance roundrobin {{ render "servers" }} {% end %}
backend echo balance roundrobin server echo-pod-1 10.42.0.7:8080 check server echo-pod-2 10.42.0.9:8080 check
$ haptic --features
Extend with templates
Add an annotation, a header rewrite, or support for a new API by editing a template.
Control the configuration
Scriggo — a Go-native template engine — renders haproxy.cfg and auxiliary files. Override a bundled
snippet or supply your own templates.
Watch any resource
HAPTIC watches any Kubernetes resource — a built-in kind or a CRD of your own. No resource type is baked into the controller; your templates decide what to read.
Validate changes
The admission webhook checks routing changes, and template tests check fixture scenarios in CI. Each pod's HAProxy binary must accept a reload before the new worker takes over.
Start with the bundled routing libraries.
Bundled libraries support Ingress, Gateway API, and HAPTIC annotations by default. When migrating, enable the compatibility library for ingress-nginx, HAProxy Technologies, or haproxy-ingress. Check its supported annotations before switching traffic.
$ haptic --extend
Let each Ingress choose a request-ID header for its
own backends. Add this snippet under
controller.config.templateSnippets in your Helm values:
controller:
config:
templateSnippets:
backend-directives-300-request-id:
template: |
{%- if ingress != nil %}
{%- var header = ingress.metadata.annotations["example.com/request-id-header"] %}
{%- if header != "" %}
{%- if !regex_search(header, "^[A-Za-z0-9-]+$") %}
{{ fail("example.com/request-id-header must contain only letters, digits, or hyphens.") }}
{%- end %}
http-request set-header {{ header }} %[uuid()]
{%- end %}
{%- end %}
Set example.com/request-id-header: "X-Request-ID"
on an Ingress. Header names use letters, digits, and hyphens. Changes to
this backend directive require a reload.
$ haptic --install
$ helm install \
haptic oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
--version 0.2.0-alpha.3
$ kubectl get haproxytemplateconfig
$ haptic --docs
Documentation
From your first Helm install to writing your own templates — deploying with Helm, watching resources, template rendering, config validation, and operating HAProxy in production.
Available versions
Playground
Edit a config and watch HAProxy render in your browser — the same engine the controller runs, no install. Tweak a resource, flip a version, share a link to exactly what you see.
// Built for operators who'd rather write a template than file a feature request.
Community project — independent and not affiliated with or endorsed by HAProxy Technologies.