Getting started¶
Overview¶
This guide installs HAPTIC and shows it turning an Ingress into a live HAProxy configuration. You'll:
- Install the controller and HAProxy with Helm
- Point an Ingress at HAPTIC and inspect the config it generates
Installing takes a few minutes on a local Kubernetes cluster. The sample-app walkthrough that follows is optional.
Want a taste first? This is a complete, minimal HAPTIC config rendering an Ingress into an HAProxy config — in your browser, no install. Click Run live, then edit the template or the Ingress and watch the output change.
The playground accepts this bare spec content directly; on a cluster the same
blocks nest under spec of the HAProxyTemplateConfig custom resource:
apiVersion: haproxy-haptic.org/v1alpha1
kind: HAProxyTemplateConfig
metadata:
name: haptic-config
namespace: haptic
spec:
credentialsSecretRef: # agent credentials Secret
name: haptic-credentials
podSelector: # which HAProxy pods receive the config
matchLabels:
app.kubernetes.io/component: loadbalancer
haproxyConfig:
template: |
# ... as above ...
watchedResources:
# ... as above ...
maps:
# ... as above ...
The Helm chart installs a complete resource of this shape for you; see the CRD Reference for every field.
Prerequisites¶
- Kubernetes cluster (1.21+) - kind, minikube, or cloud provider
- kubectl configured to access your cluster
- Helm 3.8+ (the
oci://chart reference below needs OCI registry support, generally available since Helm 3.8)
Webhook validation
A validating admission webhook is enabled by default and works out of the box — it rejects Ingress, HTTPRoute, and GRPCRoute changes that would break template rendering, using a self-signed certificate the chart issues itself (no cert-manager required). For rotation and certificate alternatives, see Webhook certificates.
Install with Helm¶
Install the controller and HAProxy using Helm:
# Install from OCI registry (deploys both controller and HAProxy pods)
helm install haptic oci://registry.gitlab.com/haproxy-haptic/haptic/charts/haptic \
--version 0.2.0-alpha.1 \
--namespace haptic --create-namespace
The Helm chart deploys:
- Controller: Watches Kubernetes resources and generates HAProxy configurations
- HAProxy pods: Load balancers, each with the HAPTIC agent alongside (2 replicas by default)
- RBAC: Permissions for watching Ingress, Service, and EndpointSlice resources
- HAProxyTemplateConfig + HAProxyTemplateLibrary: the CRD resource with the default template configuration, plus one
HAProxyTemplateLibraryper enabled template library (Ingress and Gateway API out of the box), linked from the config'sspec.libraryRefs
The chart provisions a default HTTPS certificate out of the box — a self-signed one, or a cert-manager-issued, auto-rotated one when cert-manager is present. For production domains, GitOps caveats, and alternatives, see SSL Certificates.
Verify both components are running:
# Check controller
kubectl get pods -n haptic -l app.kubernetes.io/component=controller
# Check HAProxy pods
kubectl get pods -n haptic -l app.kubernetes.io/component=loadbalancer
You should see two controller pods (the chart defaults to two replicas with leader election) and two HAProxy pods, all in Running state with full readiness (2/2 and 4/4). The controller pod runs the controller plus its validator sidecar; each HAProxy pod runs haproxy, the HAPTIC agent, the SPOA hub, and the Vector log/metrics sidecar.
HAProxy version
The chart defaults to HAProxy 3.4, the latest Long-Term Support (LTS) release. To pin a different series, set --set haproxyVersion=3.0. See HAProxy Versions for the full list and support status.
HAPTIC is running¶
That's the whole install. HAPTIC now watches Ingress and Gateway API resources with a production-ready default configuration — no templating required:
- Ingress — any Ingress with
ingressClassName: hapticis picked up automatically. HAPTIC's nativehaproxy-haptic.org/*annotation library is on by default — a best-of-breed vocabulary covering timeouts, TLS, authentication, CORS, rate-limiting, redirects, canary routing, and more. The vendor annotation libraries (HAProxy Technologies, haproxy-ingress, ingress-nginx) are opt-in aids for migrating from those controllers. See the Ingress library. - Gateway API — create a
GatewaywithgatewayClassName: hapticand attachHTTPRouteresources; see the Gateway library and GatewayClass setup.
Point your existing resources at HAPTIC and they route immediately. You only reach for templating to go beyond what these libraries already do.
Optional walkthrough: route a sample app¶
The rest of this guide deploys a sample app and confirms routing end to end. Skip it if you'll use your own Ingress or Gateway resources.
Deploy a sample app¶
Create a simple echo service:
apiVersion: apps/v1
kind: Deployment
metadata:
name: echo
namespace: default
spec:
replicas: 2
selector:
matchLabels:
app: echo
template:
metadata:
labels:
app: echo
spec:
containers:
- name: echo
image: ealen/echo-server:latest
ports:
- containerPort: 80
env:
- name: PORT
value: "80"
---
apiVersion: v1
kind: Service
metadata:
name: echo
namespace: default
spec:
selector:
app: echo
ports:
- port: 80
targetPort: 80
Save as echo-app.yaml and apply:
Create an Ingress¶
Create an Ingress that routes your test hostname to the echo service:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: echo-ingress
namespace: default
spec:
ingressClassName: haptic
rules:
- host: echo.example.local
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: echo
port:
number: 80
Save as echo-ingress.yaml and apply:
The controller automatically detects this new Ingress, renders the HAProxy configuration, validates it, and deploys it to the HAProxy pods. See What's Happening Behind the Scenes for details.
TLS for a host
This Ingress is already served over both HTTP and HTTPS. HTTPS uses the chart's default certificate — a self-signed cert out of the box — which HAPTIC binds on the https port for every Ingress, no spec.tls required. To present a host-specific certificate instead of the default, add a spec.tls entry backed by a kubernetes.io/tls Secret; to serve plain HTTP only, turn off the default HTTPS bind. See Ingress library — TLS configuration for both.
Verify the configuration¶
Check the controller logs¶
Watch the controller process the Ingress:
kubectl logs -n haptic -l app.kubernetes.io/name=haptic,app.kubernetes.io/component=controller --tail=50 -f
At the default info log level, each change produces a single consolidated Reconciliation summary line from the leader replica, for example:
level=INFO msg=Reconciliation trigger=resource_change instances=2/2 reloads=2 ops=30 render_ms=1 validate_ms=1 deploy_ms=184 total_ms=289 backend_create=2 server_create=20 server_update=8 map_update=6
It reports the trigger, how many HAProxy instances were updated (instances), the reloads and runtime operations applied (with a per-operation breakdown such as backend_create / server_create), and per-phase timings. For the individual stages — the resource change, template render, validation, and per-instance deploy — raise the controller to the debug level (see Enable debug logging).
Inspect the rendered HAProxy configuration¶
The controller writes the rendered HAProxy config to a read-only HAProxyCfg resource on every reconciliation, so you can inspect exactly what it deployed straight from the Kubernetes API — no pod access needed:
You should see:
- A frontend section with routing rules
- A backend section referencing the echo service
- Server entries pointing to the echo pod endpoints
Output vs input
HAProxyCfg (singular haproxycfg, short name hpcfg) is the controller's output — it republishes it from the templates whenever the rendered configuration changes, so editing it directly has no lasting effect and isn't advised: the next config change overwrites your edit. To change the configuration, edit the input instead — the templates, watched resources, and dataplane settings in HAProxyTemplateConfig (short names htplcfg, haptpl). Use kubectl describe rather than kubectl get -o yaml, since the latter renders multiline configs as literal \n.
Test the routing¶
Port-forward to HAProxy¶
HAProxy is running inside the cluster and isn't directly reachable from your machine. Port-forward creates a temporary tunnel from your local port to the HAProxy service:
Test the endpoint¶
In another terminal:
The echo server echoes back the request it saw. Repeat the request a few times to watch HAProxy balance across the echo pods — the HOSTNAME field (the serving pod's name) changes between responses.
What's happening behind the scenes¶
When you created the Ingress, the controller detected the change via the Kubernetes watch API and rendered the templates from the default HAProxyTemplateConfig with your Ingress data. It then decided, per pod, what the change needs: a map or server update runs on the live worker, and only a change to the configuration's structure reloads. The agent in each HAProxy pod wrote the files and ran what it was told, in parallel across the fleet — typically completing the whole cycle in under 1 second. For the full pipeline, see the Architecture Overview.
Next steps¶
Route with Ingress or Gateway API¶
The default template libraries already handle path-based routing, TLS termination, and annotation-driven configuration — no templating needed. Point your resources at HAPTIC and read the reference for what each supports:
- Ingress — the Ingress library, with HAPTIC's native
haproxy-haptic.org/*annotations on by default and the vendor compatibility libraries (HAProxy Technologies, haproxy-ingress, ingress-nginx) available opt-in for migration. - Gateway API — the Gateway library and GatewayClass setup.
Replacing another Ingress controller?¶
See Migrating to HAPTIC for the zero-downtime, one-Ingress-at-a-time cutover — and the three settings that silently break a migration if you miss them.
Customize the configuration¶
The running configuration is the HAProxyTemplateConfig resource Helm created — kubectl edit haproxytemplateconfig -n haptic haptic-config — and the CRD Reference documents every field.
Watch additional resources¶
Extend the controller to watch EndpointSlices, Secrets, ConfigMaps, or your own CRDs — see Watching Resources.
Extend with templates (advanced)¶
When the default libraries don't cover a case — a custom annotation, domain-specific logic, or an HAProxy feature they don't emit — the Templating Guide covers the template language and the resource context your templates see.
Run in production¶
For 3+ replicas, PodDisruptionBudgets, and leader election, see High Availability. For Prometheus metrics and dashboards, see Monitoring.
Troubleshooting¶
If you run into issues during setup, check these common areas:
- Controller not starting -- check logs for missing HAProxyTemplateConfig, RBAC errors, or API connectivity issues
- HAProxy pods not updating -- verify the agent container is running and credentials match
- Ingress not routing -- ensure
ingressClassName: hapticis set (or whatever you configuredingressClass.nameto) and the backend Service has endpoints
For detailed diagnosis steps, see the Troubleshooting Guide.
Clean up¶
Remove all resources created in this guide:
# Remove Ingress and echo application
kubectl delete ingress echo-ingress -n default
kubectl delete deployment echo -n default
kubectl delete service echo -n default
# Uninstall HAPTIC (removes controller, HAProxy, and all related resources)
helm uninstall haptic -n haptic
# Remove namespace
kubectl delete namespace haptic
# Remove CRDs (optional). The chart installs six — keep them in place if you plan
# to reinstall, otherwise delete all six so the API group disappears cleanly.
kubectl delete crd \
haproxytemplateconfigs.haproxy-haptic.org \
haproxytemplatelibraries.haproxy-haptic.org \
haproxycfgs.haproxy-haptic.org \
haproxygeneralfiles.haproxy-haptic.org \
haproxycrtlistfiles.haproxy-haptic.org \
haproxymapfiles.haproxy-haptic.org