Kubernetes Ingress Controllers: nginx, Traefik, and the Gateway API
January 20, 2026 · 5 min read
An Ingress controller is the front door to your Kubernetes cluster. It handles TLS termination, path-based routing, virtual hosting, rate limiting, and authentication before traffic reaches your pods. Understanding your options — nginx, Traefik, and the emerging Gateway API — lets you make the right architectural choice.
Installing nginx Ingress Controller
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.metrics.enabled=true \
--set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-type"=nlbA basic Ingress resource:
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
namespace: production
annotations:
kubernetes.io/ingress.class: nginx
spec:
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api
port:
number: 80
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80TLS with cert-manager
cert-manager automates TLS certificate issuance from Let's Encrypt:
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set installCRDs=true# ClusterIssuer for Let's Encrypt production
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
name: letsencrypt-prod
spec:
acme:
server: https://acme-v02.api.letsencrypt.org/directory
email: admin@example.com
privateKeySecretRef:
name: letsencrypt-prod-key
solvers:
- http01:
ingress:
class: nginx
---
# Ingress with automatic TLS
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: api-ingress
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
tls:
- hosts:
- api.example.com
secretName: api-tls-cert # cert-manager creates this Secret
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api
port:
number: 80cert-manager handles certificate renewal automatically.
nginx Ingress Annotations
nginx's behavior is customized via annotations:
metadata:
annotations:
# Rate limiting
nginx.ingress.kubernetes.io/limit-rps: "100"
nginx.ingress.kubernetes.io/limit-connections: "20"
# Request size limits
nginx.ingress.kubernetes.io/proxy-body-size: "8m"
# Timeouts
nginx.ingress.kubernetes.io/proxy-connect-timeout: "10"
nginx.ingress.kubernetes.io/proxy-send-timeout: "60"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
# CORS
nginx.ingress.kubernetes.io/enable-cors: "true"
nginx.ingress.kubernetes.io/cors-allow-origin: "https://app.example.com"
nginx.ingress.kubernetes.io/cors-allow-methods: "GET, POST, PUT, DELETE, OPTIONS"
# WebSocket support
nginx.ingress.kubernetes.io/proxy-http-version: "1.1"
nginx.ingress.kubernetes.io/configuration-snippet: |
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Basic auth
nginx.ingress.kubernetes.io/auth-type: basic
nginx.ingress.kubernetes.io/auth-secret: basic-auth
nginx.ingress.kubernetes.io/auth-realm: "Authentication Required"
# Canary routing
nginx.ingress.kubernetes.io/canary: "true"
nginx.ingress.kubernetes.io/canary-weight: "10"Traefik
Traefik uses CRDs (IngressRoute) for more expressive configuration:
helm install traefik traefik/traefik \
--namespace traefik \
--create-namespace# IngressRoute with middleware
apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
name: api-route
spec:
entryPoints:
- websecure
routes:
- match: Host(`api.example.com`)
kind: Rule
services:
- name: api
port: 80
middlewares:
- name: rate-limit
- name: auth-headers
tls:
certResolver: letsencrypt
---
# Rate limiting middleware
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: rate-limit
spec:
rateLimit:
average: 100
burst: 50
period: 1m
sourceCriterion:
ipStrategy:
depth: 1
---
# Header middleware
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: auth-headers
spec:
headers:
customRequestHeaders:
X-Forwarded-Proto: https
customResponseHeaders:
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Strict-Transport-Security: "max-age=31536000; includeSubDomains"Traefik's middleware system composes functionality cleanly. Create a middleware once and reference it from multiple routes.
The Gateway API (Kubernetes Standard)
The Gateway API is the successor to Ingress, now stable (v1). It's more expressive and separates infrastructure concerns (Gateway) from application routing (HTTPRoute):
# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/latest/download/standard-install.yaml
# Install a Gateway API implementation (e.g., Envoy Gateway)
helm install eg oci://docker.io/envoyproxy/gateway-helm --version v0.0.0-latest -n envoy-gateway-system --create-namespace# GatewayClass — defines the implementation
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
---
# Gateway — infrastructure: which ports/protocols to listen on
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: production
spec:
gatewayClassName: envoy
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: api-tls-cert
- name: http
port: 80
protocol: HTTP
---
# HTTPRoute — application: how to route requests
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: production
spec:
parentRefs:
- name: production-gateway
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /v2
backendRefs:
- name: api-v2
port: 80
weight: 90
- name: api-v2-canary
port: 80
weight: 10
- matches:
- path:
type: PathPrefix
value: /
backendRefs:
- name: api
port: 80The Gateway API separates concerns elegantly:
- Cluster operators manage
GatewayClassandGatewayobjects - Application teams manage
HTTPRouteobjects in their own namespaces - RBAC controls who can attach routes to gateways
Traffic splitting (canary) is a first-class feature in Gateway API, not a custom annotation.
Choosing Between nginx, Traefik, and Gateway API
| | nginx Ingress | Traefik | Gateway API | |--|--|--|--| | Maturity | Very stable | Stable | Stable (v1) | | Config style | Annotations | CRDs | CRDs | | Dashboard | No | Yes | Depends on impl | | mTLS support | Via annotations | Via middleware | Via policy | | Traffic splitting | Canary annotations | Weighted services | Weight field | | Multi-tenant | Limited | Limited | Built-in | | Ecosystem | Largest | Growing | Future standard |
Use nginx if you need battle-tested stability and the widest annotation ecosystem.
Use Traefik if you prefer CRD-based configuration and want a built-in dashboard.
Use Gateway API if you're building new infrastructure and want the standard that the ecosystem is moving toward — all major implementations (Cilium, Envoy, nginx) now support it.
The Ingress controller is one of the most consequential architectural choices in a Kubernetes deployment. Take time to evaluate based on your team's operational experience and the specific features your applications need.