The ingress controller is one of the most traffic-critical components in your cluster. A misconfigured or undersized ingress is the first place things break under load. Choosing the right one upfront saves a painful migration later.
This post covers the four controllers that matter in 2026: Nginx Ingress (ingress-nginx), Traefik, HAProxy Ingress, and Caddy (via ingress-caddy). Gateway API implementations are out of scope — that's a separate topic.
Feature Comparison
| Feature | Nginx Ingress | Traefik v3 | HAProxy Ingress | Caddy |
|---|---|---|---|---|
| TLS termination | Yes (cert-manager) | Yes (built-in ACME) | Yes (cert-manager) | Yes (built-in ACME) |
| Automatic TLS (ACME) | No (needs cert-manager) | Yes | No (needs cert-manager) | Yes |
| HTTP/2 | Yes | Yes | Yes | Yes |
| HTTP/3 (QUIC) | Yes (experimental) | Yes | No | Yes |
| WebSocket support | Yes | Yes | Yes | Yes |
| gRPC support | Yes | Yes | Yes | Yes |
| Auth middleware | Basic, OAuth (annotations) | BasicAuth, ForwardAuth | BasicAuth | BasicAuth, ForwardAuth |
| Rate limiting | Yes (annotations) | Yes (middleware CRD) | Yes (annotations) | Yes (handler) |
| Circuit breaker | No | Yes | Yes | No |
| Dashboard / UI | No | Yes (built-in) | No | No |
| CRD-based config | No (annotations) | Yes (IngressRoute) | No (annotations) | No (annotations) |
| Memory (idle, 3 replicas) | ~180 MB | ~120 MB | ~90 MB | ~75 MB |
| CPU (idle) | Low | Low | Very low | Very low |
Nginx Ingress (ingress-nginx)
Nginx Ingress is the de facto standard. It's what most tutorials assume, what most helm charts test against, and what most teams reach for by default — for good reason.
Strengths:
- ›Enormous annotation surface for fine-tuning behavior
- ›Mature, battle-tested at scale
- ›Works with every cert-manager issuer
- ›Excellent documentation and community
Weaknesses:
- ›Config reload on every Ingress change (uses nginx
-s reload) - ›No native dashboard
- ›Annotation hell at scale — configuration becomes unreadable
Install:
helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
helm install ingress-nginx ingress-nginx/ingress-nginx \
--namespace ingress-nginx \
--create-namespace \
--set controller.replicaCount=2 \
--set controller.resources.requests.cpu=100m \
--set controller.resources.requests.memory=90Mi
Basic Ingress:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "120"
spec:
ingressClassName: nginx
tls:
- hosts: [myapp.example.com]
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 3000
Use the Kubernetes Ingress Generator to scaffold this for your service.
Traefik v3
Traefik takes a different philosophy: it watches Kubernetes resources dynamically and reconfigures without reloads. This makes it operationally smoother for clusters with frequent Ingress changes (feature branches, preview environments, CI).
Traefik's built-in ACME client means you can skip cert-manager entirely for simple setups. Its IngressRoute CRD gives you much cleaner config than annotation-packed Ingress objects.
Strengths:
- ›Zero-downtime config reloads
- ›Built-in Let's Encrypt — no cert-manager needed for basic TLS
- ›Excellent dashboard for debugging routing rules
- ›Clean middleware CRDs (rate limit, auth, redirect, headers)
Weaknesses:
- ›Two config systems (Ingress annotations AND IngressRoute CRDs) creates confusion
- ›ACME cert storage in a flat file by default — needs a Kubernetes Secret backend for HA
- ›Less battle-tested at very high traffic (>50k req/s) compared to Nginx
Install:
helm repo add traefik https://helm.traefik.io/traefik
helm install traefik traefik/traefik \
--namespace traefik \
--create-namespace \
--set persistence.enabled=true \
--set persistence.size=1Gi \
--set additionalArguments[0]="--certificatesresolvers.letsencrypt.acme.email=ops@example.com" \
--set additionalArguments[1]="--certificatesresolvers.letsencrypt.acme.storage=/data/acme.json" \
--set additionalArguments[2]="--certificatesresolvers.letsencrypt.acme.tlschallenge=true"
IngressRoute example:
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: myapp
spec:
entryPoints:
- websecure
routes:
- match: Host(`myapp.example.com`)
kind: Rule
services:
- name: myapp
port: 3000
middlewares:
- name: rate-limit
tls:
certResolver: letsencrypt
See also: Traefik vs Nginx Ingress comparison.
HAProxy Ingress
HAProxy Ingress uses HAProxy as the data plane, which has the best raw throughput and lowest latency of any option here. If you're running >100k requests per second or need sub-millisecond p99 latency, this is your controller.
HAProxy also has the most mature TCP and Layer 4 capabilities — useful if you need to proxy non-HTTP traffic through your ingress layer.
Strengths:
- ›Best single-core performance of any L7 proxy
- ›Excellent TCP/SSL passthrough
- ›Very low memory footprint
- ›No config reloads for backend changes (uses HAProxy runtime API)
Weaknesses:
- ›Smaller ecosystem — fewer tutorials, less helm chart compatibility testing
- ›No built-in ACME; requires cert-manager
- ›Limited middleware ecosystem compared to Traefik
Install:
helm repo add haproxy-ingress https://haproxy-ingress.github.io/charts
helm install haproxy-ingress haproxy-ingress/haproxy-ingress \
--namespace ingress-controller \
--create-namespace
Basic config:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: myapp
annotations:
haproxy-ingress.github.io/timeout-connect: "5s"
haproxy-ingress.github.io/timeout-server: "60s"
haproxy-ingress.github.io/balance-algorithm: leastconn
spec:
ingressClassName: haproxy
tls:
- hosts: [myapp.example.com]
secretName: myapp-tls
rules:
- host: myapp.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: myapp
port:
number: 3000
Caddy (ingress-caddy)
Caddy is the simplest option for teams that want TLS to just work. Its automatic HTTPS is the smoothest of any controller here — point it at a domain and it handles cert issuance, renewal, and OCSP stapling automatically. It also has native HTTP/3 support enabled by default, which neither Nginx nor HAProxy have production-ready.
Strengths:
- ›Simplest TLS story — automatic HTTPS out of the box
- ›HTTP/3 / QUIC enabled by default
- ›Clean, readable Caddyfile configuration
- ›Very low resource usage
Weaknesses:
- ›Kubernetes integration is less mature than Nginx or Traefik
- ›Smaller annotation surface — less fine-grained control
- ›Not battle-tested at high scale in Kubernetes contexts
Install:
helm repo add ingress-caddy https://caddyserver.github.io/ingress
helm install caddy-ingress ingress-caddy/caddy-ingress-controller \
--namespace caddy-system \
--create-namespace
When to Choose Which
| Scenario | Recommendation |
|---|---|
| Default cluster, standard apps | Nginx Ingress — maximum compatibility |
| Many services, frequent deploys, preview environments | Traefik — dynamic reload, built-in ACME, dashboard |
| High-throughput API gateway, >50k req/s | HAProxy — best raw performance |
| Simple cluster, want TLS without cert-manager hassle | Caddy — easiest TLS story |
| Mixed HTTP + TCP routing | HAProxy or Traefik — both handle TCP well |
| Team already knows Nginx | Nginx Ingress — no learning curve |
Resource Planning
At idle with 2 replicas, expect these memory figures:
| Controller | Memory (2 replicas) | Notes |
|---|---|---|
| Nginx Ingress | ~180 MB total | Grows with number of Ingress objects |
| Traefik | ~120 MB total | Stable across Ingress count |
| HAProxy | ~90 MB total | Very efficient |
| Caddy | ~75 MB total | Lowest baseline |
Under load, Nginx and HAProxy scale horizontally cleanly. Use the Kubernetes Node Sizing Calculator to account for ingress controller overhead when sizing your nodes.