Migrating from NGINX Ingress Controller to HAProxy: A Step-by-Step Guide

Page content

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.

⚠️ Important: The Kubernetes SIG Network and Security Response Committee announced that Ingress NGINX will be retired in March 2026. 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:


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

graph LR A[Phase 1: Assess] --> B[Phase 2: Parallel Deploy] B --> C[Phase 3: Traffic Shift] C --> D[Phase 4: Decommission]

Phase 1: Assess Your Current Setup

First, confirm you’re using Ingress NGINX:

kubectl get pods --all-namespaces \
  --selector app.kubernetes.io/name=ingress-nginx

Then inventory your annotations:

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.

# 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):

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):

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