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
-
Fix manifests first: move to
networking.k8s.io/v1withpathType,service.name,service.port.number, and setspec.ingressClassName: nginxeverywhere. -
Rehearse on a staging cluster or
kindwith 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 -
Upgrade in staging:
helm upgrade --install ... --atomic --timeout 5m, then checkkubectl -n ingress-nginx logs deploy/ingress-nginx-controllerfor “ignoring ingress” messages and run smoke tests against every host. -
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
topologySpreadConstraintsso pods roll one at a time. -
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(orExact). - 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.