Happy Dussehra! May good always win. Happy Diwali! Wishing you a bright year ahead. Merry Christmas! Warm wishes from Vizag. Happy New Year! Here’s to a great year ahead.

Archive · Kubernetes

IngressClass and ingress-nginx 1.0

What changed in ingress-nginx 1.0: the Ingress v1 API, IngressClass resources, the deprecated annotation, and a safe upgrade path from 0.x.

3 min read

  • ingress-nginx 1.0
  • Kubernetes 1.22
  • IngressClass
  • Helm chart 4.x

ingress-nginx reached 1.0 in September 2021. Version numbers rarely matter, but this one does: it is the first release built around the stable networking.k8s.io/v1 Ingress API and the IngressClass resource, and it drops support for the removed beta APIs. Clusters running Kubernetes 1.22 need it, and older ones should move to it deliberately.

Versions in use on this date. ingress-nginx controller 1.0.x with Helm chart 4.0.x, Kubernetes 1.22 (August 2021). Version 1.0 requires Kubernetes 1.19 or newer. It builds on the ingressClassName field that became stable in 1.19.

How classes select a controller

flowchart TB
  I1[Ingress shop<br/>ingressClassName nginx] --> IC1[IngressClass nginx<br/>controller k8s.io/ingress-nginx]
  I2[Ingress admin<br/>ingressClassName nginx-internal] --> IC2[IngressClass nginx-internal<br/>controller k8s.io/ingress-nginx-internal]
  IC1 --> C1[ingress-nginx public<br/>controller-class k8s.io/ingress-nginx]
  IC2 --> C2[ingress-nginx internal<br/>controller-class k8s.io/ingress-nginx-internal]
  C1 --> LB1[Internet-facing load balancer]
  C2 --> LB2[Internal load balancer]
  I3[Ingress without class] -.->|only handled if an IngressClass is the default<br/>or watch-ingress-without-class is set| C1

An IngressClass is a cluster-scoped object that names which controller implementation handles Ingresses of that class. It replaces the old, informal annotation kubernetes.io/ingress.class.

apiVersion: networking.k8s.io/v1
kind: IngressClass
metadata:
  name: nginx
  annotations:
    ingressclass.kubernetes.io/is-default-class: "true"     # Ingresses without a class use this
spec:
  controller: k8s.io/ingress-nginx

What changed in 1.0

Area Before (0.x) 1.0
Ingress API Supports extensions/v1beta1, networking.k8s.io/v1beta1 and v1 Only networking.k8s.io/v1
Class selection --ingress-class flag and the kubernetes.io/ingress.class annotation IngressClass object plus spec.ingressClassName; the annotation is deprecated but still honoured
Controller identity Flag value --controller-class (default k8s.io/ingress-nginx)
Ingress without a class Handled by default Ignored unless an IngressClass is marked default or --watch-ingress-without-class=true
Helm chart 3.x 4.x; creates the IngressClass (controller.ingressClassResource.*)
Kubernetes versions 1.16 and up 1.19 and up
pathType Optional Required in v1 manifests

The silent trap is the fourth row: after upgrading, Ingress objects that had no class may stop being served. Check before you upgrade.

Pre-upgrade audit

kubectl get ingress -A -o custom-columns=NS:.metadata.namespace,NAME:.metadata.name,CLASS:.spec.ingressClassName,ANN:.metadata.annotations.kubernetes\\.io/ingress\\.class
kubectl get ingressclass
helm -n ingress-nginx get values ingress-nginx
kubectl -n ingress-nginx get deploy ingress-nginx-controller -o jsonpath='{.spec.template.spec.containers[0].args}' | tr ',' '\n'
pluto detect-all-in-cluster --target-versions k8s=v1.22.0

Questions to answer: which Ingresses lack a class? Which use the annotation only? Are any still defined as v1beta1 in Git or Helm values? Do custom snippets or ConfigMap options still apply (some defaults changed)?

Upgrade path

  1. Fix manifests first: move to networking.k8s.io/v1 with pathType, service.name, service.port.number, and set spec.ingressClassName: nginx everywhere.

  2. Rehearse on a staging cluster or kind with the same chart values:

    helm repo update
    helm upgrade ingress-nginx ingress-nginx/ingress-nginx -n ingress-nginx --version 4.0.x \
      --set controller.ingressClassResource.default=true --dry-run
  3. Upgrade in staging: helm upgrade --install ... --atomic --timeout 5m, then check kubectl -n ingress-nginx logs deploy/ingress-nginx-controller for “ignoring ingress” messages and run smoke tests against every host.

  4. Roll out to production during a quiet period. Existing connections may drop on controller pod restarts; run at least 2 replicas with a PodDisruptionBudget and topologySpreadConstraints so pods roll one at a time.

  5. Keep a rollback: helm rollback ingress-nginx <revision>; ensure CRDs or webhook configurations did not change in an incompatible way. Note the admission webhook (ingress-nginx-admission) validates Ingresses; if upgrades leave a stale webhook configuration, deletes and applies can fail until it is fixed.

Running two controllers

Use separate IngressClasses and controller.ingressClassResource.controllerValue per release to run public and internal controllers side by side:

helm install ingress-internal ingress-nginx/ingress-nginx -n ingress-internal --create-namespace \
  --set controller.ingressClassResource.name=nginx-internal \
  --set controller.ingressClassResource.controllerValue=k8s.io/ingress-nginx-internal \
  --set controller.service.annotations."service\.beta\.kubernetes\.io/aws-load-balancer-internal"=true \
  --set controller.ingressClass=nginx-internal

Common problems after upgrading

  • 404 default backend for a previously working host: the Ingress has no class and no default IngressClass exists.
  • Error “pathType must be specified”: add pathType: Prefix (or Exact).
  • Annotation ignored: check the nginx.ingress.kubernetes.io/ prefix and read the annotation docs; some were renamed or now disabled by default (for example configuration snippets can be disallowed by cluster policy in later releases).
  • Two controllers fighting: both watch the same class; give each its own controllerValue.