--- title: Migrating from NGINX Ingress Controller to HAProxy: A Step-by-Step Guide url: https://devopstales.github.io/kubernetes/nginx-to-haproxy-ingress-migration/ date: 2026-02-22 --- The Kubernetes community announced the retirement of Ingress NGINX with best-effort maintenance ending March 2026. This guide walks you through a safe, tested migration path to HAProxy Kubernetes Ingress Controllerβ€”with code examples, annotation mappings, and production tips. <!--more--> > ⚠️ **Important**: The Kubernetes SIG Network and Security Response Committee announced that [Ingress NGINX will be retired in March 2026](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/). After this date, there will be no security patches, bug fixes, or new releases. Existing deployments will continue to function, but running unmaintained infrastructure in production carries significant risk. If you're using `ingress-nginx` today, now is the time to plan your migration. In this post, I'll walk you through migrating to the **HAProxy Kubernetes Ingress Controller**β€”a production-ready, high-performance alternative with a clear migration path. πŸ”— **Sources**: - [Kubernetes Blog: Ingress NGINX Retirement](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/) - [HAProxy: Ingress NGINX Is Retiring](https://www.haproxy.com/blog/ingress-nginx-is-retiring) - [HAProxy Migration Assistant](https://www.haproxy.com/ingress-nginx-migration-assistant) --- ## Why Migrate? The Ingress NGINX Retirement Timeline | Date | Milestone | |------|-----------| | **Nov 2025** | Retirement announcement by Kubernetes SIG Network & SRC | | **Mar 2026** | Best-effort maintenance ends; no more releases or security fixes | | **Post-Mar 2026** | Repositories become read-only; artifacts remain available | > βœ… **Good news**: Your existing `ingress-nginx` deployments won't break. But without security updates, you'll accumulate technical debt and exposure to newly discovered vulnerabilities. ### Why HAProxy? HAProxy isn't just a "drop-in replacement"β€”it's an **upgrade**: - πŸš€ **2Γ— higher throughput** with lower CPU usage (verified in public benchmarks) - πŸ” **Safer configuration model**: No arbitrary config snippets; structured annotations and CRDs - ♻️ **Zero-downtime reloads**: Apply config changes without dropping connections - πŸ“Š **Rich native Prometheus metrics**β€”no sidecars required - 🌐 **First-class Layer 4 + Layer 7 support**: TCP, gRPC, HTTP/S, WebSocket - πŸ†“ **100% open source**, with optional enterprise support via HAProxy One --- ## Migration Strategy: 4 Phases to Zero Downtime ``` β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β” β”‚ Phase 1: Assess │───>β”‚ Phase 2: Parallel │───>β”‚ Phase 3: Traffic │───>β”‚ Phase 4: Decommissionβ”‚ β”‚ β”‚ β”‚ Deploy β”‚ β”‚ Shift β”‚ β”‚ β”‚ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ ``` ## Phase 1: Assess Your Current Setup First, confirm you're using Ingress NGINX: ```bash kubectl get pods --all-namespaces \ --selector app.kubernetes.io/name=ingress-nginx ``` Then inventory your annotations: ```bash kubectl get ingress -A -o jsonpath='{range .items[*]}{.metadata.name}{"\t"}{.metadata.annotations}{"\n"}{end}' \ | grep nginx.ingress.kubernetes.io ``` ## Phase 2: Deploy HAProxy Ingress Controller in Parallel Install HAProxy alongside your existing controllerβ€”no disruption required. ```bash # Add the HAProxy Helm repo helm repo add haproxytech https://haproxytech.github.io/helm-charts helm repo update # Install in a dedicated namespace helm install haproxy-ingress haproxytech/kubernetes-ingress \ --namespace haproxy-controller \ --create-namespace \ --set controller.ingressClass=haproxy \ --set controller.service.type=LoadBalancer \ --set controller.replicaCount=2 ``` ## Phase 3: Migrate Ingress Resources (With Examples) **Before (NGINX):** ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: kubernetes.io/ingress.class: nginx nginx.ingress.kubernetes.io/rewrite-target: / nginx.ingress.kubernetes.io/ssl-redirect: "true" spec: tls: - hosts: - my-app.tld secretName: my-app-tls rules: - host: example.com http: paths: - path: /app pathType: Prefix backend: service: name: app-service port: number: 80 ``` **After (HAProxy):** ```yaml apiVersion: networking.k8s.io/v1 kind: Ingress metadata: name: example-ingress annotations: kubernetes.io/ingress.class: haproxy haproxy.org/path-rewrite: / # ← rewrite-target equivalent haproxy.org/ssl-redirect: "true" # ← ssl-redirect equivalent spec: tls: - hosts: - my-app.tld secretName: my-app-tls rules: - host: example.com http: paths: - path: /app pathType: Prefix backend: service: name: app-service port: number: 80 ``` | Nginx | HAproxy | |-------|---------| |nginx.ingress.kubernetes.io/load-balance|haproxy.org/load-balance| |nginx.ingress.kubernetes.io/backend-protocol|haproxy.org/backend-protocol| |nginx.ingress.kubernetes.io/proxy-connect-timeout|haproxy.org/proxy-connect-timeout| |nginx.ingress.kubernetes.io/cors-allow-origin|haproxy.org/cors-allow-origin| |nginx.ingress.kubernetes.io/affinity|haproxy.org/affinity| ## Phase 4: Decommission NGINX ### Step 1: Preserve the IngressClass Before uninstalling, preserve the `nginx` IngressClass to avoid breaking existing resources: ```bash helm upgrade ingress-nginx ingress-nginx \ --repo https://kubernetes.github.io/ingress-nginx \ --namespace ingress-nginx \ --reuse-values \ --set-json 'controller.ingressClassResource.annotations={"helm.sh/resource-policy": "keep"}' ``` ### Step 2: Delete Admission Webhooks ```bash kubectl delete -A ValidatingWebhookConfiguration ingress-nginx-admission kubectl delete -A MutatingWebhookConfiguration ingress-nginx-admission ``` ### Step 3: Uninstall NGINX ```bash helm uninstall ingress-nginx --namespace ingress-nginx kubectl delete namespace ingress-nginx ```