- Published on
HAProxy Ingress Controller — A Battle-Tested Load Balancer Enters Kubernetes
- Authors

- Name
- Youngju Kim
- @fjvbn20031
- Introduction
- The Ingress Controller Landscape — The Reality of 2026
- Two HAProxy Ingress Projects — A Distinction You Must Never Confuse
- Why HAProxy — Strengths of the Data Plane
- Architecture — How the Controller Drives HAProxy
- The Three Configuration Layers — Global / Defaults / Backend
- Deployment Walkthrough — Installing the Official Controller with Helm
- Basic Routing — Writing the Ingress Resource
- Annotations — The Ones You Use Most in Practice
- Dynamic Reload — Runtime API and Hitless Reload
- TCP Mode and TLS Passthrough
- Rate Limiting and ACLs — The Power of Stick Tables
- TLS and cert-manager Integration
- The Flow Toward the Gateway API — The Next Standard After Ingress
- Operations and Tuning — Observability, HA, Performance
- Pitfalls and Troubleshooting
- Wrapping Up
- References
Introduction
Choosing a Kubernetes ingress controller really means deciding what will guard the front door of your cluster (north-south traffic). For a long time the de facto standard for this role was ingress-nginx, but in 2026 the landscape has shifted considerably.
First, the Kubernetes Ingress API itself has been frozen. No new features are landing in it anymore, and all new development is moving to the Gateway API. Second, ingress-nginx, the longtime standard, has effectively shifted into maintenance mode, and during that transition a string of security issues (CVEs) stemming from annotation-based configuration injection have been reported repeatedly, shaking operators' confidence.
Against this backdrop, HAProxy is drawing renewed attention. HAProxy is an L4/L7 load balancer that was proven in large-scale traffic environments long before Kubernetes existed. The core appeal is simple: you reuse a data plane that has already been running in production for over twenty years, directly as your ingress.
There is one trap, however. The thing people call the "HAProxy ingress controller" is actually two separate projects. One is the official project built directly by HAProxy Technologies, and the other is a community project that Joao Morais has maintained for a long time. The two have similar names but completely different configuration models and annotation prefixes. In this article we clearly distinguish the two projects while covering the architecture, dynamic reload, TCP/TLS passthrough, rate limiting, Helm deployment, and the Gateway API transition in a code-centric way.
The Ingress Controller Landscape — The Reality of 2026
Let us first set the big picture. An ingress controller is split into a data plane (the proxy that actually handles traffic) and a control plane (the part that watches the Kubernetes API and generates configuration). Which proxy engine you choose determines the character of both performance and features.
| Controller | Data plane engine | State in 2026 | Strengths | Caveats |
|---|---|---|---|---|
| ingress-nginx | NGINX + Lua | Maintenance mode, accumulating CVEs | Overwhelming user base, abundant docs | Feature stagnation, security-patch focused |
| HAProxy (haproxytech official) | HAProxy + Data Plane API | Actively developed | Proven stability, hitless reload, official support | Separate annotation prefix, learning required |
| HAProxy (jcmoraisjr community) | HAProxy | Actively developed | Rich features, embedded mode | Single-maintainer dependency |
| Traefik | Native (Go) | Active | Gateway API leader, dynamic config | Weaker than HAProxy/NGINX at extreme load |
| Envoy-based (Contour, EG) | Envoy | Active, Gateway-API centric | gRPC/HTTP2 strengths, xDS | Resource usage, complexity |
| Cilium | Envoy + eBPF | Active | Kernel level, Gateway API | CNI coupling, learning curve |
If we summarize the position of the HAProxy family in one sentence, it is this: it is the compromise you pick when you want to move toward the new standard (Gateway API) like Traefik or Envoy, while still guaranteeing a proven, high-performance data plane like NGINX.
Two HAProxy Ingress Projects — A Distinction You Must Never Confuse
This is the first thing this article needs to nail down. The two projects that show up when you search for "HAProxy Ingress" are different pieces of software.
| Aspect | haproxytech/kubernetes-ingress | jcmoraisjr/haproxy-ingress |
|---|---|---|
| Owner | HAProxy Technologies (company official) | Joao Morais (community) |
| Repository | github.com/haproxytech/kubernetes-ingress | github.com/jcmoraisjr/haproxy-ingress |
| Docs site | haproxy.com/documentation/kubernetes-ingress | haproxy-ingress.github.io |
| Annotation prefix | haproxy.org | haproxy-ingress.github.io |
| Config update method | Data Plane API + Runtime API centric | Template based + Runtime API |
| Commercial support | Tied to HAProxy Enterprise Kubernetes | Community based |
| Character | Company maintained, clear release cadence | Feature rich, long track record |
Both projects use the same HAProxy engine as their data plane, but their control plane implementations and user interfaces (annotations, ConfigMap keys) differ. For example, even for the same "request timeout setting" the annotation key is different.
Official (haproxytech): haproxy.org/timeout-client: "30s"
Community (jcmoraisjr): haproxy-ingress.github.io/timeout-client-fin: "30s"
Therefore, before you paste in an example you found on the internet, you must first confirm which project you are using. Unless otherwise noted, the body of this article is based on the official project (haproxytech/kubernetes-ingress), and we will point out community-project differences as they come up.
A summary selection guide:
- If you need company-level official support, clear release governance, and an upgrade path to HAProxy Enterprise, the official project (haproxytech) is the natural choice.
- If you value the fine-grained features accumulated over a long time (global config snippets, varied auth options, etc.) and a community track record, the jcmoraisjr project is also strong.
- For a new adoption where you can obtain vendor support per company policy, we recommend the official project for the long term.
Why HAProxy — Strengths of the Data Plane
The reason HAProxy is appealing as an ingress data plane ultimately comes down to the maturity of the engine itself.
First, performance and stability. HAProxy has a single-process, event-driven (multi-threaded event loop) architecture that has long been optimized to handle hundreds of thousands of concurrent connections with low CPU and memory. The fundamentals such as connection pooling, keep-alive reuse, and backend health checks are rock solid.
Second, rich L7 features. You can implement sophisticated ACL (Access Control List) based routing, header manipulation, rate limiting, stick-table based state tracking, and circuit-breaker-like behavior (backend blocking) purely through configuration.
Third, observability. HAProxy's stats page and Prometheus exporter provide very detailed per-backend response times, queue lengths, and connection states.
Fourth, dynamic reload. We cover this in detail below, but through the Runtime API and hitless reload (seamless reload), HAProxy can apply configuration changes without dropping existing connections. In an ingress environment where backends (Pods) change constantly, this is a decisive advantage.
Architecture — How the Controller Drives HAProxy
The internal structure of the official controller operates along the following flow.
+-----------------------------------------------------------------------+
| HAProxy Ingress Controller Pod |
| |
| +-------------------------+ +----------------------------+ |
| | Controller (Go) | | HAProxy process | |
| | | | | |
| | - K8s API watch | -----> | - frontend (80/443) | |
| | Ingress / Service / | Data | - backend (Pod IP:port) | |
| | Endpoints / Secret / | Plane | - ACL / map / stick-table | |
| | ConfigMap | API | - stats / runtime socket | |
| | - generate/validate | <----- | | |
| | - Runtime API calls | state | | |
| +-------------------------+ +----------------------------+ |
+-----------------------------------------------------------------------+
^ |
| watch (informer) | forward
| v
+------------------+ +-------------------------+
| Kubernetes API | | Backend Pods (Service) |
| Server | | app-a / app-b / ... |
+------------------+ +-------------------------+
client ──HTTP/HTTPS/TCP──> [frontend] ──ACL routing──> [backend] ──> Pod
The key point is that the controller (a Go process) and the data plane (the HAProxy process) are separate within the same Pod. The controller watches the Kubernetes API via informers, and when it detects changes to Ingress/Service/Endpoints/Secret/ConfigMap, it applies them to HAProxy in one of two ways.
- Frequent changes such as endpoints (Pod IPs) are applied immediately via the Runtime API by adding/removing servers without a reload.
- Structural changes such as adding a frontend or altering ACL structure regenerate the configuration and trigger a hitless reload.
Distinguishing between "changes that require a reload" and "changes that do not" is the key concept for operating HAProxy ingress well.
The Three Configuration Layers — Global / Defaults / Backend
HAProxy ingress configuration is split into three layers according to scope of application. Understanding this structure makes it clear whether a given setting belongs in the ConfigMap or in an annotation.
| Layer | Applies to | Where to set | Examples |
|---|---|---|---|
| Global | The entire HAProxy process | ConfigMap (controller-wide) | maxconn, ssl defaults, nbthread |
| Defaults | Defaults for all frontends/backends | ConfigMap | default timeouts, log format |
| Ingress/Service | Individual routing unit | Ingress or Service annotation | path routing, rate limit, backend protocol |
Because of this priority order, the pattern of setting things globally but overriding for a specific service falls out naturally. For example, you can set the default timeout to 30 seconds in the ConfigMap, while giving an upload API 300 seconds via an annotation.
Deployment Walkthrough — Installing the Official Controller with Helm
Now let us actually install it. The official project provides a Helm chart. The reference environment is Kubernetes v1.32.
First, add the repo.
helm repo add haproxytech https://haproxytech.github.io/helm-charts
helm repo update
helm search repo haproxytech/kubernetes-ingress
The simplest install looks like this.
kubectl create namespace haproxy-controller
helm install haproxy-ingress haproxytech/kubernetes-ingress \
--namespace haproxy-controller \
--set controller.kind=Deployment \
--set controller.replicaCount=2
In production it is better to manage everything explicitly with a values file. Here is a values example commonly used in practice.
controller:
kind: Deployment
replicaCount: 2
# Use a LoadBalancer-type Service instead of the node host network
service:
type: LoadBalancer
externalTrafficPolicy: Local # preserve client source IP
# IngressClass settings — essential when multiple controllers coexist
ingressClass: haproxy
ingressClassResource:
enabled: true
default: false
# ConfigMap values that inject HAProxy global/default settings
config:
timeout-client: "30s"
timeout-server: "30s"
timeout-connect: "5s"
maxconn: "100000"
ssl-redirect: "true"
# Resource requests/limits
resources:
requests:
cpu: 500m
memory: 256Mi
limits:
cpu: "2"
memory: 512Mi
# Expose Prometheus metrics
serviceMonitor:
enabled: true
# Spread Pods apart
podDisruptionBudget:
enable: true
minAvailable: 1
helm upgrade --install haproxy-ingress haproxytech/kubernetes-ingress \
--namespace haproxy-controller \
-f values-prod.yaml
Verification after install:
kubectl -n haproxy-controller get pods
kubectl -n haproxy-controller get svc
kubectl get ingressclass
Let us pause to explain why externalTrafficPolicy is set to Local here. The default, Cluster, routes through node-to-node SNAT and loses the client's real IP. With Local, traffic is only handled on the node that received it, so the source IP is preserved. If you want to use rate limiting or IP-based ACLs, this IP preservation is effectively a prerequisite.
Basic Routing — Writing the Ingress Resource
A basic host/path-based routing example.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress
namespace: demo
annotations:
haproxy.org/load-balance: "roundrobin"
spec:
ingressClassName: haproxy
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-frontend
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: api-backend
port:
number: 8080
Note that you must always specify ingressClassName. In a cluster where multiple ingress controllers coexist, omitting it means either no controller handles the Ingress or an unintended controller picks it up.
Annotations — The Ones You Use Most in Practice
The annotation prefix for the official project is haproxy.org. The commonly used ones are summarized below.
| Annotation | Meaning | Example value |
|---|---|---|
| haproxy.org/load-balance | Load balancing algorithm | roundrobin, leastconn |
| haproxy.org/timeout-client | Client timeout | 30s |
| haproxy.org/timeout-server | Backend response timeout | 60s |
| haproxy.org/ssl-redirect | Redirect HTTP to HTTPS | "true" |
| haproxy.org/server-proto | Backend protocol | h2 (HTTP/2) |
| haproxy.org/path-rewrite | Path rewriting | /api/(.*) to /v1/ |
| haproxy.org/rate-limit-requests | Request rate limit | "100" |
| haproxy.org/whitelist | Allowed IP ranges | 10.0.0.0/8 |
For example, to talk to the backend over HTTP/2 and raise the response timeout, write the following.
metadata:
annotations:
haproxy.org/server-proto: "h2"
haproxy.org/timeout-server: "120s"
haproxy.org/check: "enabled"
Here we again emphasize the difference from the community project (jcmoraisjr). Even with the same intent, the prefix and key name differ.
Official (haproxytech): haproxy.org/server-proto: "h2"
Community (jcmoraisjr): haproxy-ingress.github.io/backend-protocol: "h2"
Dynamic Reload — Runtime API and Hitless Reload
This section is the true differentiator of HAProxy ingress. In an ingress environment, Pods scale in and out, and rolling updates constantly change IPs. If you fully restart the proxy every time you reflect these changes in the data plane, connections drop.
HAProxy solves this problem with two mechanisms.
First, the Runtime API. HAProxy accepts runtime commands over a Unix socket. The controller applies operations such as adding/removing backend servers or changing weights immediately, without a reload.
[Pod scale-out occurs]
Endpoints change detected
│
▼
Controller sends commands via Runtime API:
set server backend_app/srv3 addr 10.244.2.17 port 8080
set server backend_app/srv3 state ready
│
▼
HAProxy activates the new server immediately, no reload
(no impact on existing connections)
Second, hitless reload (seamless reload). When you have to change the configuration file itself, such as adding a frontend or altering ACL structure, a reload is unavoidable, but HAProxy performs that reload without downtime. It launches a new process with the new configuration while keeping the old process alive until in-flight connections finish, and the listening socket is shared by both processes. Thanks to SO_REUSEPORT and a socket handover mechanism, new connections are not rejected even at the moment of reload.
The important operational implication is this: "endpoint changes do not trigger a reload, so you can relax about them, but frequent ConfigMap/structural changes can trigger reloads." If the reload frequency is too high, old processes that have not finished their in-flight connections can pile up, so you control the maximum lifetime of old processes with a value like hard-stop-after.
controller:
config:
hard-stop-after: "30m" # max lifetime of an old process after reload
TCP Mode and TLS Passthrough
If you only handle HTTP, ingress is enough, but there are cases where you must expose TCP directly, such as databases or message brokers. HAProxy ingress supports this in two ways.
First, TCP services. The official controller connects TCP on arbitrary ports to backend services through a separate ConfigMap.
apiVersion: v1
kind: ConfigMap
metadata:
name: haproxy-kubernetes-ingress-tcp
namespace: haproxy-controller
data:
# Format: "frontPort": "namespace/service:port"
"5432": "demo/postgresql:5432"
"6379": "demo/redis:6379"
This makes the controller create TCP frontends on ports 5432 and 6379 and forward them to each service. Note that these ports must be exposed externally via a LoadBalancer Service or host ports.
Second, TLS passthrough. A normal ingress terminates TLS at the controller, but when the backend itself must handle TLS (for example, keeping mTLS all the way through to the backend), the controller must pass the traffic through without decrypting it. In this case it routes by inspecting the SNI (Server Name Indication).
TLS termination (default):
client ──TLS──> [HAProxy: decrypt] ──plaintext/re-encrypt──> backend
TLS passthrough:
client ──TLS──> [HAProxy: read SNI only, no decrypt] ──TLS as-is──> backend
(certificate lives on the backend)
Passthrough operates as a kind of TCP mode. The controller inspects the SNI field of the SSL handshake with an ACL and sends each host to a different backend. However, in this case the controller cannot see the HTTP headers, so path-based routing and header manipulation are impossible. You must remember the limitation that only host (SNI) level routing is possible.
Rate Limiting and ACLs — The Power of Stick Tables
One of HAProxy's real strengths is state tracking using stick tables. A stick table is an in-memory key-value store that can track per-source-IP request counts, connection counts, error rates, and so on. On top of this you implement rate limiting and abuse blocking.
The basic annotation-based rate limit looks like this.
metadata:
annotations:
haproxy.org/rate-limit-requests: "100"
haproxy.org/rate-limit-period: "1m"
haproxy.org/rate-limit-status-code: "429"
This setting returns 429 when more than 100 requests per minute come from a source IP. For this to be meaningful, the true client IP must be preserved via the externalTrafficPolicy: Local emphasized earlier.
ACLs enable more fine-grained control, such as allowing a specific path only from a specific IP range, or only letting requests with a certain header through. In the official controller you can use whitelist/blacklist annotations, and you can inject raw HAProxy configuration via global config snippets.
metadata:
annotations:
# Allow the admin path only from internal ranges
haproxy.org/whitelist: "10.0.0.0/8,192.168.0.0/16"
Conceptually, the HAProxy configuration fragment generated internally looks like the following. (This is auto-generated by the controller; you do not write it by hand.)
frontend https
bind *:443 ssl crt /etc/haproxy/certs/
# stick table: track request count per source IP
stick-table type ip size 100k expire 1m store http_req_rate(1m)
http-request track-sc0 src
# 429 when exceeding 100 per minute
acl too_many sc_http_req_rate(0) gt 100
http-request deny deny_status 429 if too_many
# ACL allowing only the internal network
acl internal_net src 10.0.0.0/8 192.168.0.0/16
acl admin_path path_beg /admin
http-request deny if admin_path !internal_net
use_backend api_backend if { path_beg /api }
default_backend web_backend
Being able to express this level of traffic control through annotations and a ConfigMap alone is the power of the HAProxy data plane.
TLS and cert-manager Integration
In practice, the standard is to issue and renew certificates automatically with cert-manager. HAProxy ingress follows the standard Ingress TLS spec, so it integrates naturally with cert-manager.
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: web-ingress-tls
namespace: demo
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"
haproxy.org/ssl-redirect: "true"
spec:
ingressClassName: haproxy
tls:
- hosts:
- app.example.com
secretName: app-example-com-tls
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-frontend
port:
number: 80
Once cert-manager passes the ACME (Let's Encrypt) challenge and produces a certificate as a Secret, the controller watches that Secret and applies it to HAProxy. Because the certificate is applied via the Runtime API on renewal, it is usually reflected without a reload.
The Flow Toward the Gateway API — The Next Standard After Ingress
As mentioned in the introduction, the Ingress API has been frozen and the successor standard is the Gateway API. The Gateway API was designed to address head-on the limitations of ingress: the proliferation of annotations, the absence of role separation, and weak L4 support.
The key difference is role-based resource separation.
| Ingress model | Gateway API model | Owning role |
|---|---|---|
| IngressClass | GatewayClass | Infrastructure/platform team |
| (none) | Gateway (listeners, ports, TLS) | Cluster operator |
| Ingress rules | HTTPRoute / TCPRoute / GRPCRoute | App development team |
[Gateway API traffic flow]
GatewayClass (controller = haproxy)
│ implements
▼
Gateway ──> listener definition (e.g. 443/HTTPS, TLS cert)
│ references
▼
HTTPRoute ──> host/path matching ──> backendRefs (Service)
│
▼
client ──> [Gateway:HAProxy frontend] ──> [HTTPRoute rules] ──> Service
The official HAProxy controller has gradually added Gateway API support and can handle core resources such as HTTPRoute and TCPRoute. The recommended practical strategy is as follows.
- There is no need to migrate existing Ingress resources that work well right away. The Ingress API is not disappearing; it simply will not gain new features.
- For new workloads, especially when you need advanced features such as L4 (TCP/UDP) routing or traffic splitting (canary), evaluate the Gateway API first.
- Always verify in the documentation, before adoption, how mature the controller's Gateway API support is (which Route types and which filters it supports).
A simple HTTPRoute example follows.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: haproxy-gateway
namespace: demo
spec:
gatewayClassName: haproxy
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: app-example-com-tls
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
namespace: demo
spec:
parentRefs:
- name: haproxy-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-backend
port: 8080
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: web-frontend
port: 80
Operations and Tuning — Observability, HA, Performance
Observability
HAProxy's stats and Prometheus metrics are the starting point of troubleshooting. Turning on the ServiceMonitor lets you collect per-backend response times, active connection counts, queue buildup, reload counts, and more. In particular, we recommend putting the following metrics on a dashboard.
- Per-backend 5xx rate and average response time
- Number of requests waiting in the queue (a signal of backend saturation)
- Reload frequency (diagnoses whether config changes are too frequent)
- Active/idle connection counts and utilization against maxconn
High Availability Setup
Run the controller with at least 2 replicas and limit simultaneous termination with a PodDisruptionBudget. To survive node failure, spread across different nodes/zones with topologySpreadConstraints or anti-affinity. Expose external entry via a LoadBalancer Service (cloud) or MetalLB (on-prem), and the cloud LB in front distributes traffic across the controller replicas.
external client
│
▼
[cloud LB / MetalLB VIP]
│ │
▼ ▼
[HAProxy Pod 1] [HAProxy Pod 2] (different nodes/zones)
│ │
└──────┬───────┘
▼
backend service Pods
Performance Tuning Points
- Set nbthread to match the node's core count to take advantage of multiple cores.
- Set maxconn high enough for your workload, while also considering memory and file-descriptor limits.
- Turn on keep-alive and backend connection reuse to reduce handshake overhead.
- If the backend is HTTP/2 or gRPC, specify it with server-proto to prevent protocol downgrades.
controller:
config:
nbthread: "4"
maxconn: "100000"
timeout-http-keep-alive: "60s"
Pitfalls and Troubleshooting
Here are the problems frequently encountered in practice along with their causes.
First, mixing annotations from the two projects. If you pasted an internet example and the setting does not take effect, nine times out of ten it belongs to the project with the other prefix. Do not confuse haproxy.org with haproxy-ingress.github.io.
Second, missing ingressClassName. In a cluster with multiple controllers, failing to specify the class causes routing to be dropped or to conflict. Make specifying ingressClassName on every Ingress your standard.
Third, loss of client IP. If your rate limit or IP ACL sees all traffic as coming from a single IP (the node IP), then either externalTrafficPolicy is Cluster or the upstream LB is not properly forwarding the PROXY protocol / X-Forwarded-For. Inspect the source-IP preservation path end to end.
Fourth, a reload storm. If reload frequency looks abnormally high in the metrics, configuration or ConfigMaps may be being changed too often by automation. Endpoint changes should not cause reloads under normal operation, so if reloads are frequent, track down the cause of structural changes.
Fifth, attempting path routing under TLS passthrough. With passthrough the controller does not decrypt, so it cannot see the HTTP path. If you need path-based routing, you must use termination mode.
Sixth, traffic blackholing due to unconfigured backend health checks. If you do not enable check, traffic can go to dead Pods. Explicitly enable health checks via annotations and verify consistency with the readinessProbe.
The starting point of diagnosis is always the HAProxy configuration and logs inside the controller Pod.
# Check controller logs
kubectl -n haproxy-controller logs deploy/haproxy-ingress -c haproxy-ingress
# Enter the controller Pod and inspect the generated config
kubectl -n haproxy-controller exec -it deploy/haproxy-ingress -- cat /etc/haproxy/haproxy.cfg
# Query current server state via the runtime socket (inside the Pod)
echo "show servers state" | socat stdio /var/run/haproxy-runtime-api.sock
Wrapping Up
The greatest value of the HAProxy ingress controller lies in bringing a proven data plane directly to the Kubernetes entry point. Thanks to hitless reload and the Runtime API, you can keep traffic flowing reliably without dropping connections even in an environment where Pods change constantly, and stick-table-based rate limiting and ACLs provide a substantial level of traffic control without a separate WAF.
The first thing to decide when adopting it is whether you are using the official (haproxytech) project or the community (jcmoraisjr) project. The two projects have different annotation models, so you must make this choice clearly and then write your configuration consistently. For a new adoption that needs vendor support, consider the official project; if you want long community validation and rich features, consider jcmoraisjr.
Finally, do not forget the big picture of 2026. The Ingress API has been frozen and the future is the Gateway API. A realistic dual strategy is to keep your well-running Ingress while designing new workloads and advanced routing with the Gateway API. In this transition period, HAProxy is a dependable option that lets you carry your stability forward while moving to the new standard.
References
- HAProxy official documentation: https://www.haproxy.com/documentation
- HAProxy Kubernetes Ingress Controller (official): https://www.haproxy.com/documentation/kubernetes-ingress
- haproxytech/kubernetes-ingress repository: https://github.com/haproxytech/kubernetes-ingress
- HAProxy Ingress (jcmoraisjr community): https://haproxy-ingress.github.io
- jcmoraisjr/haproxy-ingress repository: https://github.com/jcmoraisjr/haproxy-ingress
- HAProxy Runtime API documentation: https://www.haproxy.com/documentation/haproxy-runtime-api
- Kubernetes Ingress concepts: https://kubernetes.io/docs/concepts/services-networking/ingress/
- Kubernetes Gateway API: https://gateway-api.sigs.k8s.io/
- cert-manager documentation: https://cert-manager.io/docs/
- HAProxy Technologies Helm Charts: https://github.com/haproxytech/helm-charts