Template Libraries¶
Template libraries are modular, composable configuration packages that extend HAProxy functionality. Each library provides specific features that can be enabled or disabled based on your requirements.
Overview¶
HAPTIC uses a library-based architecture where YAML configuration files are merged at Helm render time. This enables:
- Modularity: Enable only the features you need
- Extensibility: Add custom configuration via extension points
- Maintainability: Each library focuses on a specific concern
- Customization: Override or extend library behavior through values.yaml
See the full library stack compose into one HAProxy config live:
Available Libraries¶
| Library | Default | Purpose |
|---|---|---|
| Base | Enabled | Core HAProxy configuration, extension point definitions; disabling drops the haproxyConfig the other libraries plug into |
| SSL | Enabled | TLS certificate management, HTTPS frontend |
| Ingress | Enabled | Kubernetes Ingress resource support |
| Gateway API | Enabled | Gateway API (HTTP, gRPC, TLS and TCP routes) support |
| ingress-annotations-compat | Enabled | Shared scaffold consumed by the Ingress vendor annotation libraries below (level 2.5) |
| haproxytech | Enabled | haproxy.org/* annotations (haproxytech/kubernetes-ingress compat) |
| haproxy-ingress | Enabled | haproxy-ingress.github.io/* annotations (jcmoraisjr/haproxy-ingress compat) |
| nginx-ingress | Disabled | nginx.ingress.kubernetes.io/* annotations (kubernetes/ingress-nginx compat) |
| spoa-hub | Auto | HAProxy-side wiring for the SPOA hub sidecar (auto-loaded when spoaHub.enabled: true or any spoaHub.plugins.<X>.enabled is truthy) |
Enabling and Disabling Libraries¶
Configure libraries in your values.yaml:
controller:
templateLibraries:
base:
enabled: true # Default — disabling drops the haproxyConfig the other libraries plug into
ssl:
enabled: true # TLS/HTTPS support
ingress:
enabled: true # Kubernetes Ingress
gateway:
enabled: true # Gateway API
haproxytech:
enabled: true # haproxy.org annotations
haproxyIngress:
enabled: true # HAProxy Ingress compatibility
nginxIngress:
enabled: false # nginx-ingress annotation compatibility
config:
routing:
regexMatchOrder: default # "default" or "last" — see configuration.md
Library Merge Order¶
Libraries are merged in a specific order, with later libraries overriding earlier ones:
1. base.yaml (lowest priority)
2. ssl.yaml
3. ingress.yaml
4. gateway/
5. ingress-annotations-compat.yaml (level 2.5 - Ingress-only shared scaffold)
6. haproxytech.yaml
7. haproxy-ingress/
8. nginx-ingress/
9. spoa-hub/ (auto-loaded when SPOA hub sidecar is enabled)
10. controller.config.* (highest priority - your values.yaml overrides for templateSnippets / maps / files / sslCertificates / haproxyConfig / validationTests / watchedResources)
Your custom configuration in controller.config always takes precedence.
Extension Points¶
Extension points are the core mechanism for library extensibility. The base library defines hook points where other libraries (or your custom configuration) can inject content.
How Extension Points Work¶
The base library uses render_glob "prefix-*" to automatically include all template snippets matching a glob pattern:
This includes all snippets whose names start with backends- (e.g. backends-500-ingress, backends-500-gateway, any user-provided backends-*). Snippets render in alphabetical order, so numeric prefixes control execution order — see the snippet priority numbering table below.
Available Extension Points¶
| Extension Point | Prefix Pattern | Where Included | Purpose |
|---|---|---|---|
| Global Settings | global-settings-* |
Inside global section |
Global directives (logging, process, paths, SSL tuning) |
| Defaults Settings | defaults-settings-* |
Inside defaults section |
Defaults directives (options, balance, timeouts, errorfiles) |
| Features | features-* |
Early in config | Feature initialization, SSL setup |
| Global Top | global-top-* |
After defaults |
Userlists, peers, global elements |
| Frontend Extra | frontend-extra-* |
After frontend bind, before routing | Early frontend directives (options, captures, ACLs) |
| Frontend Matchers | frontend-matchers-advanced-* |
Frontend routing | Method, header, query matching |
| Frontend Filters | frontend-filters-* |
HTTP frontend | Request/response processing |
| Custom Frontends | frontends-* |
After HTTP frontend | HTTPS, TCP frontends |
| Custom Backends | backends-* |
Before default backend | Backend definitions |
| Backend Directives | backend-directives-* |
Within backends | Per-backend configuration |
| Host Map | map-host-* |
host.map | Host routing entries |
| Path Exact Map | map-path-exact-* |
path-exact.map | Exact path entries |
| Path Prefix Exact Map | map-pfxexact-* |
path-prefix-exact.map | Prefix exact entries |
| Path Prefix Map | map-path-prefix-* |
path-prefix.map | Prefix path entries |
| Path Regex Map | map-path-regex-* |
path-regex.map | Regex path entries |
| Weighted Backend Map | map-weighted-backend-* |
weighted-multi-backend.map | Weighted routing |
| Status Patches | status-patches-* |
After features, before backends | Resource status updates (side effects only, no config output) |
Injecting Custom Configuration¶
Add custom snippets in your values.yaml to inject configuration at extension points:
controller:
config:
templateSnippets:
# Override default timeouts (replaces defaults-settings-300-timeouts)
defaults-settings-300-timeouts:
template: |
timeout connect 5000
timeout client 30000
timeout server 30000
timeout tunnel 600000
timeout http-request 10000
# Add custom global tuning directives
global-settings-500-tuning:
template: |
tune.bufsize 262144
no-memory-trimming
# Add early frontend directives (matches frontend-extra-*)
frontend-extra-custom-captures:
template: |
capture request header X-Request-ID len 64
# Inject into frontend (matches frontend-filters-*)
frontend-filters-security:
template: |
# Block admin paths from external IPs
http-request deny if { path_beg /admin } !{ src 10.0.0.0/8 }
# Inject into backends (matches backends-*)
backends-maintenance:
template: |
backend maintenance
http-request return status 503 content-type text/html string "<h1>Maintenance</h1>"
# Inject into host map (matches map-host-*)
map-host-custom:
template: |
# Custom host routing
legacy.example.com legacy.example.com
Library Configuration via extraContext¶
extraContext is a parameter bag exposed to every snippet (read with
extraContext | dig("key") | fallback("default")). It carries chart-computed
values (ports, the HAProxy service name, …) plus anything you set under
controller.config.templatingSettings.extraContext.
Bundled libraries ship sensible defaults for their tunables, which you can
override here. For example, the nginx-ingress library's HTTP→HTTPS redirect
status code — HAPTIC's equivalent of ingress-nginx's global http-redirect-code
(default 308):
controller:
config:
templatingSettings:
extraContext:
nginxHttpRedirectCode: "301" # override the library default of 308
A value you set here always wins over the library's default. Custom snippets read any key the same way:
Snippet Priority¶
Snippets within a render_glob pattern execute in alphabetical order. Priority is encoded in the snippet name via a numeric prefix:
controller:
config:
templateSnippets:
# Runs early (alphabetically sorts before the 500-range)
features-050-my-init:
template: |
{# Initialize something first #}
# Runs after the core 500-range snippets
features-700-my-finalize:
template: |
{# Finalize after other initialization #}
Reserved numeric ranges used by the built-in libraries:
| Range | Purpose |
|---|---|
| 000-099 | Infrastructure / initialization |
| 100-199 | Feature registration |
| 200-499 | Security, CORS, header manipulation, redirects |
| 500-599 | Core functionality (ingress, gateway) |
| 600-799 | Compatibility layers (haproxy-ingress, nginx-ingress) |
| 900-999 | Finalization / cleanup |
To override a built-in snippet, use the same key name; values-file entries take precedence over library entries during merge.
Which Libraries Use Which Extension Points¶
| Library | Extension Points Used |
|---|---|
| Base | Defines all extension points; provides global-settings-*, defaults-settings-* snippets |
| SSL | features-*, frontends-*, backends-*, global-top-* |
| Ingress | features-*, backends-*, map-host-*, map-path-*, status-patches-* |
| Gateway | features-*, backends-*, map-*, frontend-matchers-advanced-*, frontend-filters-*, status-patches-* |
| haproxytech | global-top-*, backend-directives-*, frontend-filters-* |
| haproxy-ingress | features-*, map-path-*, map-pfxexact-*, backend-directives-*, frontend-filters-*, global-top-*, backends-* |
| nginx-ingress | features-*, backends-*, global-top-*, backend-directives-*, frontend-filters-* |
Custom Libraries¶
You can create custom libraries by watching any Kubernetes resource and implementing extension point patterns against it. Because HAPTIC is resource-agnostic, a plain ConfigMap becomes HAProxy config the same way an Ingress does — watch it, then emit into backends-* and map-host-* from a template snippet.
In your own values.yaml, drop the same watchedResources and templateSnippets under controller.config — the bundled base library already provides the render_glob invocations, so you only supply the snippets.
Library Architecture¶
Each library sits at a hierarchy level that determines which other
libraries it may reference and the order it merges in. Lower levels merge
first; higher levels override. controller.config from values.yaml is
applied last and overrides anything below it.
┌──────────────────────────────────────────────────────────────────────┐
│ values.yaml │
│ (highest priority) │
└──────────────────────────────────────────────────────────────────────┘
▲
┌──────────────────────────────────────────────────────────────────────┐
│ Level 3 — Vendor annotation libraries │
│ haproxytech.yaml haproxy-ingress/ nginx-ingress/ │
│ (each compat-layer for one ingress controller's annotation set) │
└──────────────────────────────────────────────────────────────────────┘
▲
┌──────────────────────────────────────────────────────────────────────┐
│ Level 2.5 — Ingress-annotations-compat scaffold │
│ ingress-annotations-compat.yaml (shared macros for Ingress vendor │
│ libraries above; Ingress-scoped) │
└──────────────────────────────────────────────────────────────────────┘
▲
┌──────────────────────────────────────────────────────────────────────┐
│ Level 2 — Resource libraries │
│ ingress.yaml gateway/ │
│ (Kubernetes Ingress) (Gateway API HTTP/gRPC/ │
│ TLS/TCP routes) │
└──────────────────────────────────────────────────────────────────────┘
▲
┌──────────────────────────────────────────────────────────────────────┐
│ Level 1 — SSL/TLS infrastructure │
│ ssl.yaml │
└──────────────────────────────────────────────────────────────────────┘
▲
┌──────────────────────────────────────────────────────────────────────┐
│ Level 0 — Resource-agnostic core │
│ base.yaml (defines extension points; lowest priority) │
└──────────────────────────────────────────────────────────────────────┘
The spoa-hub/ library wires the SPOA hub sidecar into HAProxy
and is auto-loaded whenever the sidecar is enabled (by an explicit
spoaHub.enabled: true or any spoaHub.plugins.<X>.enabled truthy).
It plugs into the same extension points as the level-3 libraries
above.
Each library's own page (linked from the Available Libraries table above) documents its snippets, tunables, and extension points in detail.