An Ingress resource routes no traffic on its own: it describes rules something else has to translate into real configuration. That something, the Ingress Controller, has to be installed separately, and the choice between the two most common options, nginx-ingress and Traefik, comes down to a configuration model, not raw performance that’s rarely comparable in any meaningful way for most workloads.

nginx-ingress: a Kubernetes resource, an nginx configuration

nginx-ingress translates every Ingress resource into classic nginx configuration, regenerated and reloaded on every change. The configuration model stays nginx’s own, exposed via annotations on the Kubernetes resource:

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  annotations:
    nginx.ingress.kubernetes.io/proxy-body-size: "50m"
    nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
  ingressClassName: nginx
  rules:
    - host: api.example.com

The advantage: a team already familiar with nginx finds a known vocabulary, and the project’s maturity (the most historically deployed) means abundant documentation for nearly every edge case. The structural limit: every configuration change triggers an nginx reload that can, under heavy traffic and many rules, cause brief drops in in-flight connections.

Traefik: native Kubernetes configuration, hot reload

Traefik starts from a different principle: configuration happens natively through Kubernetes Custom Resources (IngressRoute), with a hot reload that restarts no process at all.

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: api-route
spec:
  routes:
    - match: Host(`api.example.com`)
      kind: Rule
      services:
        - name: api-service
          port: 80
      middlewares:
        - name: rate-limit

Hot reload eliminates the risk of a drop when a rule changes, a real benefit on a cluster with frequent deployments. The trade-off: IngressRoute is a Traefik-proprietary extension, not the standard Ingress resource, which reduces portability if the team ever wants to switch controllers without rewriting everything.

The real criterion: change frequency, not the brand

A cluster where routing rules change rarely (a handful of deployments per week) never feels nginx-ingress’s limit: reloads are too infrequent for their cost to matter. A cluster with very frequent deployments, preview environments continuously created and torn down (covered in the article on ephemeral preview environments per PR), directly feels Traefik’s hot-reload benefit, because the number of configuration changes itself becomes significant.

What doesn’t depend on the choice

Both controllers rely on MetalLB the same way in an on-premise context: the Ingress Controller runs behind a LoadBalancer Service, whether nginx-ingress or Traefik is driving it. The controller choice changes nothing about the network layer that exposes it to the outside world.

Takeaway

nginx-ingress and Traefik answer the same need through two opposite models: annotation-based configuration on a standard resource versus native Custom Resources with hot reload. The deciding criterion is the actual frequency of routing rule changes, not a brand preference or a raw performance comparison rarely relevant in practice. This choice, like the LoadBalancer service exposing it, gets made early in a Kubernetes migration.