| |

LFCE 142 ๐Ÿง Kubernetes โ€” Ingress Controllers

โฑ๏ธ Estimated read: 10 min | |

An Ingress Controller is the running software that makes Ingress resources work. The Ingress resource is a rule set; the controller is the process that watches for those rules, translates them into the configuration of an actual proxy or load balancer, and routes the traffic. Without a controller, an Ingress manifest is accepted by the API server and does nothing. The Kubernetes documentation states this directly: “In order for an Ingress to work in your cluster, there must be an ingress controller running” .

This chapter covers the controller landscape: the controllers the Kubernetes project maintains, the third-party implementations that dominate production deployments, the IngressClass mechanism that selects which controller handles which Ingress, and the installation path for the two most common choices (NGINX and Traefik). It also covers the important context that the Ingress API is frozen and the project recommends Gateway API for new work.

Key point: The Kubernetes project has frozen the Ingress API and recommends Gateway API for new deployments. Ingress remains stable and will not be removed, but it will receive no further development. The controller you choose still matters, and the most widely deployed controller, ingress-nginx, reached end-of-life in March 2026 .


Why the controller choice matters

The API freeze problem. The Ingress API is generally available and subject to the stability guarantees of GA APIs, but it is no longer being developed. The Kubernetes documentation states: “The Ingress API is no longer being developed, and will have no further changes or updates made to it” . The project recommends Gateway API instead. A new deployment in 2026 should consider whether Ingress is the right choice or whether the Gateway API’s more expressive routing model is a better fit.

The implementation fragmentation problem. Kubernetes maintains only the AWS and GCE ingress controllers. Everything else is third-party. The list includes NGINX, Traefik, HAProxy, Contour, Kong, Istio, Cilium, and dozens more . Each controller has its own annotations, its own configuration model, and its own behavior for edge cases. The Ingress resource is the portable part; the controller-specific annotations are not.

The end-of-life problem. The ingress-nginx project reached end-of-life in March 2026. It no longer receives releases, bug fixes, or security updates. The OVHcloud documentation states: “The project no longer receives releases, bug fixes, or security updates and should not be deployed for new workloads” . A cluster still running ingress-nginx is running an unmaintained component that terminates TLS and routes all external traffic.


a. The IngressClass and controller selection

The IngressClass resource is how a cluster maps an Ingress to the controller that should implement it. The class has a spec.controller field that identifies the controller implementation, and an Ingress references the class by name with ingressClassName .

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
spec:
  controller: k8s.io/ingress-nginx

A cluster can run multiple controllers. Each controller watches for Ingress resources with a matching class. An Ingress without a class uses the cluster’s default class if one exists, marked with the ingressclass.kubernetes.io/is-default-class: "true" annotation . If no default exists and the Ingress has no class, no controller processes it.

The NGINX Ingress Controller documentation notes that the controller “won’t start without an IngressClass resource” . This is a common installation failure: the controller Deployment is created, the Pods crash-loop, and the reason is a missing IngressClass.


b. Installing NGINX Ingress Controller

NGINX is the most widely deployed controller, but the installation path matters because the project is end-of-life. The F5 NGINX Ingress Controller (from nginx.org) is a different project from the Kubernetes ingress-nginx (from kubernetes.github.io). The F5 controller is actively maintained; the Kubernetes ingress-nginx is not.

The F5 controller is installed with Helm from the OCI registry:

helm install my-release oci://ghcr.io/nginx/charts/nginx-ingress --version 2.7.3

After installation, verify the IngressClass:

kubectl get ingressclasses
# NAME    CONTROLLER                     AGE
# nginx   nginx.org/ingress-controller   10m

The NGINX documentation warns that the Helm chart installs required CRDs, and without them the controller Pods will not become Ready . The --skip-crds flag is only appropriate if the custom resources are not used.

The Kubernetes ingress-nginx project, which is the one that reached EOL, was installed from the https://kubernetes.github.io/ingress-nginx Helm repository . That repository is no longer receiving updates.


c. Installing Traefik

Traefik is the controller that many migration guides recommend as the replacement for ingress-nginx. It is actively maintained, supports the Ingress resource, and is installed with Helm.

helm repo add traefik https://traefik.github.io/charts
helm repo update traefik
helm upgrade --install traefik traefik/traefik \
  --version 41.0.2 \
  --namespace traefik \
  --create-namespace \
  --wait --timeout 3m

The installation creates a LoadBalancer Service that provisions a cloud load balancer if the cluster supports it. The external IP is available in the Service status:

kubectl get service traefik -n traefik --watch

An Ingress that uses Traefik sets ingressClassName: traefik:

spec:
  ingressClassName: traefik
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80

Traefik also supports cert-manager for automatic TLS certificate provisioning. The ClusterIssuer uses the traefik class for the HTTP-01 challenge solver .


Complete Example Session

# ============================================
# PART 1: THE INGRESSCLASS
# ============================================
apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: traefik
spec:
  controller: traefik.io/ingress-controller
# ============================================
# PART 2: INSTALL TRAEFIK
# ============================================
helm repo add traefik https://traefik.github.io/charts
helm repo update traefik
helm upgrade --install traefik traefik/traefik \
  --namespace traefik \
  --create-namespace \
  --wait --timeout 3m
# ============================================
# PART 3: VERIFY THE CONTROLLER
# ============================================
kubectl rollout status deployment/traefik -n traefik
kubectl get ingressclasses
# NAME      CONTROLLER                      AGE
# traefik   traefik.io/ingress-controller    2m
# ============================================
# PART 4: CHECK THE EXTERNAL IP
# ============================================
kubectl get service traefik -n traefik --watch
# NAME      TYPE           EXTERNAL-IP
# traefik   LoadBalancer   203.0.113.10
# ============================================
# PART 5: THE APPLICATION SERVICE
# ============================================
apiVersion: v1
kind: Service
metadata:
  name: app
spec:
  selector:
    app: app
  ports:
    - port: 80
      targetPort: 8080
# ============================================
# PART 6: THE INGRESS
# ============================================
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: app-ingress
spec:
  ingressClassName: traefik
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80
# ============================================
# PART 7: VERIFY THE INGRESS
# ============================================
kubectl get ingress app-ingress
# NAME          CLASS     HOSTS             ADDRESS         PORTS
# app-ingress   traefik   app.example.com   203.0.113.10    80
# ============================================
# PART 8: TEST THE ROUTING
# ============================================
curl -H "Host: app.example.com" http://203.0.113.10/
# ============================================
# PART 9: CERT-MANAGER CLUSTERISSUER
# ============================================
apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: letsencrypt-prod
spec:
  acme:
    email: admin@example.com
    server: https://acme-v02.api.letsencrypt.org/directory
    privateKeySecretRef:
      name: letsencrypt-prod-key
    solvers:
      - http01:
          ingress:
            class: traefik
# ============================================
# PART 10: TLS ON THE INGRESS
# ============================================
spec:
  ingressClassName: traefik
  tls:
    - hosts: [app.example.com]
      secretName: app-tls
  rules:
    - host: app.example.com
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: app
                port:
                  number: 80

The ten parts covered the IngressClass, installing Traefik, verifying the controller, checking the external IP, the application Service, the Ingress, verification, testing, the ClusterIssuer, and TLS.


Quick Reference

Controller landscape

ControllerMaintained byStatus
AWS Load Balancer ControllerKubernetes projectMaintained
GCE Ingress ControllerKubernetes projectMaintained
NGINX Ingress (F5)F5/nginx.orgMaintained
ingress-nginx (Kubernetes)CommunityEnd-of-life (March 2026)
TraefikTraefik LabsMaintained
HAProxyHAProxy TechnologiesMaintained
ContourCommunityMaintained
KongKongMaintained

IngressClass fields

FieldPurpose
metadata.nameThe class name referenced by ingressClassName
spec.controllerIdentifies the controller implementation
Default annotationingressclass.kubernetes.io/is-default-class: "true"

Installation commands

ControllerCommand
Traefikhelm upgrade --install traefik traefik/traefik --namespace traefik --create-namespace
NGINX (F5)helm install my-release oci://ghcr.io/nginx/charts/nginx-ingress

Verification

CheckCommand
IngressClasskubectl get ingressclasses
Controller Podskubectl get pods -n <namespace>
External IPkubectl get service <controller> -n <namespace>
Ingress statuskubectl get ingress

Best Practices

โœ… Do This:

# Verify the IngressClass after installation
kubectl get ingressclasses                                     # โœ…
# Set ingressClassName explicitly on every Ingress
spec:
  ingressClassName: traefik                                     # โœ…
# Use a maintained controller for new deployments
# Traefik, HAProxy, or F5 NGINX                                  # โœ…

โŒ Don’t Do This:

# Deploy ingress-nginx for new workloads
# It reached end-of-life in March 2026                           # โŒ
# Omit ingressClassName and assume a default exists
# If no default is marked, the Ingress is inert                 # โŒ
# Install a controller without checking the CRDs
# The Pods will not become Ready                                 # โŒ

Common Pitfalls

PitfallWhy It HappensFix
Controller Pods crash-loopMissing IngressClass or CRDsCreate the IngressClass; install the CRDs
Ingress has no effectNo controller matches the classCheck kubectl get ingressclasses and the ingressClassName
External IP is <pending>No cloud load balancer or MetalLBInstall a load balancer or use NodePort
503 from the controllerBackend Service has no endpointsCheck kubectl get endpoints
Security vulnerabilitiesingress-nginx is EOLMigrate to a maintained controller

Real-World Examples

1. Traefik installation

helm upgrade --install traefik traefik/traefik --namespace traefik --create-namespace

2. Traefik IngressClass

kubectl get ingressclasses

3. Traefik Ingress

spec:
  ingressClassName: traefik

4. NGINX (F5) installation

helm install my-release oci://ghcr.io/nginx/charts/nginx-ingress

5. NGINX IngressClass

spec:
  controller: nginx.org/ingress-controller

6. Default class annotation

metadata:
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"

7. Check controller Pods

kubectl get pods -n traefik

8. Check external IP

kubectl get service traefik -n traefik

9. Test routing

curl -H "Host: app.example.com" http://<external-ip>/

10. Migrate from ingress-nginx

ingress-nginx is EOL. Use Traefik or HAProxy.

Visual

The controller selection

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  INGRESS                                                     โ”‚
โ”‚    ingressClassName: traefik                                 โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  INGRESSCLASS (traefik)                                      โ”‚
โ”‚    spec.controller: traefik.io/ingress-controller            โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  TRAEFIK CONTROLLER (running Pod)                            โ”‚
โ”‚    watches Ingress resources with class "traefik"            โ”‚
โ”‚    configures the proxy                                      โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The controller landscape (2026)

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  MAINTAINED                      END-OF-LIFE                 โ”‚
โ”‚                                                              โ”‚
โ”‚  Traefik                         ingress-nginx (Kubernetes) โ”‚
โ”‚  HAProxy                         (EOL March 2026)            โ”‚
โ”‚  F5 NGINX                                                    โ”‚
โ”‚  Contour                                                     โ”‚
โ”‚  Kong                                                        โ”‚
โ”‚  AWS/GCE (Kubernetes project)                                โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Installation verification

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  helm install ...                                            โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  kubectl get ingressclasses                                  โ”‚
โ”‚    โ†’ IngressClass exists?                                    โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  kubectl get pods -n <namespace>                             โ”‚
โ”‚    โ†’ Pods Running?                                           โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ–ผ                                                      โ”‚
โ”‚  kubectl get service -n <namespace>                          โ”‚
โ”‚    โ†’ External IP assigned?                                   โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

The EOL migration

โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
โ”‚  ingress-nginx (EOL)                                         โ”‚
โ”‚       โ”‚                                                      โ”‚
โ”‚       โ”œโ”€โ”€ No security patches                                โ”‚
โ”‚       โ”œโ”€โ”€ No bug fixes                                       โ”‚
โ”‚       โ””โ”€โ”€ No releases                                        โ”‚
โ”‚                                                              โ”‚
โ”‚  Migration options:                                          โ”‚
โ”‚    โ”œโ”€โ”€ Traefik (Ingress provider)                            โ”‚
โ”‚    โ”œโ”€โ”€ HAProxy Kubernetes Ingress Controller                 โ”‚
โ”‚    โ””โ”€โ”€ Gateway API (the project's recommendation)            โ”‚
โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜

Summary

ItemValue
Controller purposeWatches Ingress resources, configures the proxy
RequiredYes; Ingress is inert without a controller
IngressClassSelects the controller by spec.controller
Default classingressclass.kubernetes.io/is-default-class: "true"
Kubernetes-maintained controllersAWS, GCE
Third-party controllersNGINX, Traefik, HAProxy, Contour, Kong, etc.
ingress-nginx statusEnd-of-life (March 2026)
API statusFrozen; Gateway API recommended for new work

Key takeaways:

  • An Ingress Controller is required for Ingress to work. The resource is a rule set; the controller is the running software that implements it. Without a controller, the Ingress is accepted by the API server and routes nothing .
  • The Ingress API is frozen. The Kubernetes project has stopped developing it and recommends Gateway API for new work. Ingress remains stable and will not be removed, but new features will not be added .
  • The IngressClass maps an Ingress to a controller. The class’s spec.controller field identifies the implementation. An Ingress references the class by name. A default class can be marked with an annotation .
  • The controller installation must include the IngressClass and CRDs. The NGINX controller “won’t start without an IngressClass resource,” and the Helm chart installs CRDs that the Pods need to become Ready .
  • ingress-nginx is end-of-life. The Kubernetes ingress-nginx project reached EOL in March 2026. It receives no security patches or bug fixes. New deployments should use a maintained controller like Traefik, HAProxy, or F5 NGINX .
  • Traefik is a common migration target. It is actively maintained, supports the Ingress resource, and is installed with Helm. The installation creates a LoadBalancer Service, and an Ingress with ingressClassName: traefik is routed by the controller.

Remember: The Ingress Controller is the component that turns the Ingress rules into real traffic routing. The resource declares the rules; the controller implements them. The IngressClass selects which controller handles which Ingress. The API is frozen and the most common implementation is end-of-life, so the controller choice in 2026 is also a decision about long-term maintenance. For new deployments, consider Gateway API, and if Ingress is required, choose a controller that is actively maintained.

Was this helpful?


Stop using slow, ad-bloated tool sites! ๐Ÿคฎ

๐Ÿ”Ž Search “KandZ Tools” on Google to use many professional utilities for free.

KandZ.me is the ultimate minimalist hub for:
โœ… Finance (Mortgage, Interest, Inflation)
โœ… Tech (Base64, JSON, Dev Suite, IP)
โœ… Health (BMI, BMR, TDEE)
โœ… Productivity (Timer, Workspace, QR)

โšก๏ธ Fast & Private
๐Ÿ”’ No data leaves your device
๐Ÿ’Ž 100% Free

๐Ÿ”— Use it now: https://tools.kandz.me
๐Ÿ”– Bookmark itโ€”youโ€™ll need it later!