← All articles

Kubernetes Ingress Controllers: nginx, Traefik, and the Gateway API

January 20, 2026 · 5 min read

kubernetesdevops

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"=nlb

A 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: 80

TLS 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: 80

cert-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: 80

The Gateway API separates concerns elegantly:

  • Cluster operators manage GatewayClass and Gateway objects
  • Application teams manage HTTPRoute objects 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.