- Authors
- Name
- 1. Introduction: Your Cluster Is in Danger
- 2. [Takeaway 1] The Countdown of a Time Bomb: The Official Retirement of Ingress NGINX
- 3. [Takeaway 2] Escaping the Annotation Swamp
- 4. [Takeaway 3] Separation of Roles: Peaceful Coexistence of Infrastructure Administrators and Developers
- 5. [Takeaway 4] Breaking Vendor Lock-in: Achieving Portability
- 6. [Takeaway 5] The Performance Leap: Full-Scale Adoption of HTTP/3 and QUIC
- 7. Practical Migration Guide
- 8. Comprehensive Migration Checklist
- 9. Conclusion: It Is Time to Act
- 10. References
1. Introduction: Your Cluster Is in Danger
In March 2026, one of the most widely used infrastructure components in the Kubernetes ecosystem officially reaches the end of its life. This is the EOL (End of Life) of the Ingress NGINX Controller (kubernetes/ingress-nginx).
This is not a simple version upgrade or minor change. Approximately 41-50% of internet-connected Kubernetes clusters use Ingress NGINX, and it ships as the default ingress controller on major platforms including RKE2, IBM Cloud, and Alibaba ACK. The fact that this controller will no longer receive security patches, bug fixes, or any releases means that hundreds of thousands of production clusters will be left unprotected.
"In March 2026, Kubernetes will retire Ingress NGINX, a piece of critical infrastructure for about half of cloud native environments... Half of you will be affected. You have two months left to prepare... We cannot overstate the severity of this situation or the importance of beginning migration..."
-- Kat Cosgrove, CNCF Ambassador & DevRel Engineer
This article analyzes the essence of this transition from 5 key perspectives:
- The Countdown of a Time Bomb -- The official retirement of Ingress NGINX and its aftermath
- Escaping the Annotation Swamp -- Structural flaws and Gateway API's solution
- Separation of Roles -- Peaceful coexistence of infrastructure administrators and developers
- Breaking Vendor Lock-in -- Achieving portability
- The Performance Leap -- Full-scale adoption of HTTP/3 and QUIC
┌─────────────────────────────────────────────────────────────────┐
│ 2026 Kubernetes Networking Transition Roadmap │
├──────────┬──────────┬──────────┬──────────┬──────────┬──────────┤
│ 2025.03 │ 2025.11 │ 2026.03 │ 2026.06 │ 2026.11 │ 2027+ │
│ │ │ │ │ │ │
│ Ingress │ Official │ ■ EOL ■ │ Gateway │ AKS/GKE │ Ingress │
│ Nightmare│ Retire │ Security │ API v1.5 │ Extended │ API │
│ CVE │ Announce │ Patches │ (Est.) │ Support │ Residual│
│ │ │ End │ │ Ends │ Low │
│ GW API │ GW API │ │ │ │ │
│ v1.2 │ v1.4 GA │ │ │ │ │
└──────────┴──────────┴──────────┴──────────┴──────────┴──────────┘
▲ ▲ ▲
│ │ │
CVE-2025-1974 Community Support Cloud Vendor
CVSS 9.8 Fully Ends Extended Support Ends
2. [Takeaway 1] The Countdown of a Time Bomb: The Official Retirement of Ingress NGINX
2.1 What Is Being Retired: Precise Scope Definition
To prevent confusion, let's clarify something. What is being retired is the kubernetes/ingress-nginx controller project. The GA (General Availability) Ingress API itself (networking.k8s.io/v1) is still supported in Kubernetes. However, since the most popular implementation that actually runs the Ingress API is disappearing, the practical impact is enormous.
┌─────────────────────────────────────────────────────────┐
│ Retirement Scope Clarification │
│ │
│ ✗ Retiring: kubernetes/ingress-nginx controller │
│ (Community-maintained project) │
│ │
│ ✓ Continuing: Ingress API (networking.k8s.io/v1) │
│ F5 NGINX Ingress Controller (Commercial) │
│ Other third-party Ingress implementations │
│ │
│ ★ New Standard: Gateway API (gateway.networking.k8s.io)│
└─────────────────────────────────────────────────────────┘
2.2 Why Now: Accumulation of Structural Problems
The retirement of the Ingress NGINX project is not a sudden decision. It is the result of structural problems that have accumulated over years reaching a critical point:
1) Maintainer Exhaustion
The project has long relied on just one or two volunteers. Having critical infrastructure that affects half of internet-connected clusters depend on individuals' spare time is not a sustainable model.
"For a long time, we've considered big companies or end user companies that rely on open source not contributing back to be a moral issue. But now, I think it's very easy to make the argument that not contributing back to open source is, in fact, a security issue. You have to start considering open source contributions as part of your security plan."
-- Kat Cosgrove
2) Unmanageable Technical Debt
Features once praised for their flexibility, particularly the ability to inject arbitrary NGINX configuration through snippets annotations, are now recognized as unmanageable security vulnerabilities. This issue will be discussed in more detail later.
3) IngressNightmare: The Impact of CVE-2025-1974
CVE-2025-1974, discovered in March 2025, dramatically exposed the structural vulnerability of Ingress NGINX.
2.3 IngressNightmare: The Worst Kubernetes Vulnerability Ever
CVE-2025-1974 is not a single vulnerability but an attack chain composed of 5 chained vulnerabilities (CVE-2025-1097, CVE-2025-1098, CVE-2025-24513, CVE-2025-24514, CVE-2025-1974).
| CVE ID | CVSS | Description |
|---|---|---|
| CVE-2025-24514 | 8.8 | Configuration injection via auth-url annotation |
| CVE-2025-1097 | 8.8 | Configuration injection via auth-tls-match-cn annotation |
| CVE-2025-1098 | 8.8 | Configuration injection via mirror-target/mirror-host annotations |
| CVE-2025-24513 | 4.8 | Lack of auth-url file path validation |
| CVE-2025-1974 | 9.8 | Unauthenticated Remote Code Execution (Unauthenticated RCE) |
Attack Scenario:
Attacker (with Pod network access)
│
▼
┌──────────────────────────┐
│ Admission Webhook │ ← Accessible without authentication
│ (port 8443) │
└──────────┬───────────────┘
│ Send malicious Ingress object
▼
┌──────────────────────────┐
│ NGINX Config Injection │ ← Inject arbitrary NGINX directives
│ (Leveraging CVE-2025- │
│ 1097/1098/24514) │
└──────────┬───────────────┘
│ Achieve RCE via crafted config
▼
┌──────────────────────────┐
│ Controller Pod Takeover │ ← CVE-2025-1974
│ (CVSS 9.8) │
└──────────┬───────────────┘
│ Steal ServiceAccount token
▼
┌──────────────────────────┐
│ Access All Cluster │ ← Can view Secrets from
│ Secrets & Cluster │ all namespaces
│ Takeover │
└──────────────────────────┘
According to Wiz Research, approximately 43% of cloud environments were exposed to this vulnerability, and more than 6,500 clusters, including Fortune 500 companies, had vulnerable Admission Controllers exposed to the public internet.
The key lesson from this incident is clear: the architecture of annotation-based configuration injection itself is a fundamental security threat.
2.4 EOL Timeline and Platform-Specific Impact
| Timeframe | Event | Impact |
|---|---|---|
| March 2025 | IngressNightmare (CVE-2025-1974) disclosed | Emergency patches released (v1.12.1, v1.11.5) |
| November 2025 | Official Ingress NGINX retirement announced | Community migration advisory begins |
| March 2026 | Community support fully ends | All security patches, bug fixes, releases cease |
| November 2026 | AKS Application Routing extended support ends | Azure managed environments also lose support |
| After November 2026 | All official/extended support ends | Fully self-managed responsibility |
"Best-effort maintenance will continue until March 2026. Afterward, there will be no further releases, no bugfixes, and no updates to resolve any security vulnerabilities that may be discovered."
-- Tabitha Sable, Kubernetes Security Response Committee
Cloud Vendor Response Status:
| Cloud Vendor | Response Strategy | Extended Support | Recommended Alternative |
|---|---|---|---|
| Azure (AKS) | ingress-nginx support within Application Routing Add-on | Until November 2026 (security patches only) | Gateway API + NGINX Gateway Fabric |
| GCP (GKE) | GKE Gateway Controller provided | Recommends transition to vendor's own implementation | GKE Gateway Controller |
| AWS (EKS) | AWS Load Balancer Controller | Already separated to own implementation | AWS Gateway API Controller |
| Self-Hosted | Direct migration required | None (ends March 2026) | NGINX Gateway Fabric, Envoy Gateway, etc. |
For organizations running self-hosted environments, the situation is most urgent. No official patches will be provided for any security vulnerabilities discovered after March 2026.
3. [Takeaway 2] Escaping the Annotation Swamp
3.1 Structural Flaws of Annotation-Based Configuration
The most fundamental limitation of the Ingress API is lack of expressiveness. The Ingress resource spec only supports the most basic features: host-based routing and path-based routing. As a result, virtually all advanced features needed in practice rely on annotations -- an unstructured mechanism.
Analyzing the fundamental problems of annotations reveals the following:
1) Absence of Type Safety
Annotations are simple key: value string pairs. Without schema validation, typos, incorrect value formats, and wrong key names are not discovered until deployment time.
# Can you spot the typos?
metadata:
annotations:
# Typo: "rewrite-target" → "rewrite-traget"
nginx.ingress.kubernetes.io/rewrite-traget: /$2
# Type error: should be a number but string passed
nginx.ingress.kubernetes.io/proxy-read-timeout: 'sixhundred'
# Wrong prefix: "nginx.ingress.kubernetes.io" → "nginx.kubernetes.io"
nginx.kubernetes.io/ssl-redirect: 'true'
All three of the above are applied without any errors during kubectl apply. Problems only manifest as unexpected behavior at runtime, and debugging can take hours.
2) Vendor Lock-in Amplification
Each Ingress Controller uses its own annotation namespace:
# ingress-nginx specific
nginx.ingress.kubernetes.io/canary: 'true'
nginx.ingress.kubernetes.io/canary-weight: '20'
# Traefik specific
traefik.ingress.kubernetes.io/router.middlewares: default-rate-limit@kubernetescrd
# HAProxy specific
haproxy.org/rate-limit-requests: '100'
# AWS ALB specific
alb.ingress.kubernetes.io/target-type: ip
While implementing the same functionality (canary deployments, rate limiting, etc.), completely different annotation syntax is used. When migrating from one controller to another, all annotations must be manually converted.
3) Impossibility of Audit and Governance
Annotations are listed flat in the Ingress resource metadata. Identifying security-sensitive settings in an Ingress object with dozens of annotations is virtually impossible. Restricting specific annotations via RBAC is also impossible -- if you can write annotations, you can write all annotations.
3.2 The snippets Annotation: A Vulnerability by Design
The extreme case of the annotation problem is nginx.ingress.kubernetes.io/configuration-snippet and nginx.ingress.kubernetes.io/server-snippet. These annotations allow direct injection of arbitrary NGINX configuration directives.
# Dangerous: Allows injection of arbitrary NGINX configuration
metadata:
annotations:
nginx.ingress.kubernetes.io/configuration-snippet: |
# Intended normal use: add custom headers
more_set_headers "X-Custom-Header: value";
# Malicious use: exfiltrate server information
# proxy_pass http://internal-service.kube-system.svc.cluster.local;
# Malicious use: bypass authentication
# satisfy any;
# allow all;
This feature was the direct cause of multiple security vulnerabilities including CVE-2023-5043. With only the permission to create Ingress objects, it was effectively possible to manipulate the entire NGINX process configuration. This is because the configuration-snippet annotation is inserted directly into the location block of the NGINX configuration file, and server-snippet into the server block.
3.3 Gateway API's Annotation-less Model
Gateway API solves this problem with structured API types. Instead of relying on annotations, it expresses configuration through typed CRD fields.
Comparison Example: HTTPS Redirect and Path Rewriting
Legacy Ingress + Annotations approach:
# Ingress approach: relies on annotations
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: 'true'
nginx.ingress.kubernetes.io/rewrite-target: /$2
nginx.ingress.kubernetes.io/proxy-read-timeout: '600'
nginx.ingress.kubernetes.io/proxy-send-timeout: '600'
nginx.ingress.kubernetes.io/proxy-body-size: '100m'
nginx.ingress.kubernetes.io/cors-allow-origin: 'https://example.com'
nginx.ingress.kubernetes.io/cors-allow-methods: 'GET, POST, PUT'
nginx.ingress.kubernetes.io/cors-allow-headers: 'Content-Type, Authorization'
nginx.ingress.kubernetes.io/enable-cors: 'true'
nginx.ingress.kubernetes.io/limit-rps: '100'
nginx.ingress.kubernetes.io/canary: 'true'
nginx.ingress.kubernetes.io/canary-weight: '20'
spec:
ingressClassName: nginx
tls:
- hosts:
- app.example.com
secretName: app-tls
rules:
- host: app.example.com
http:
paths:
- path: /api(/|$)(.*)
pathType: ImplementationSpecific
backend:
service:
name: api-service
port:
number: 80
Gateway API approach:
# Gateway API approach: structured spec
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
spec:
parentRefs:
- name: production-gateway
hostnames:
- app.example.com
rules:
# Rule 1: HTTPS redirect (expressed via spec, not annotations)
- filters:
- type: RequestRedirect
requestRedirect:
scheme: https
statusCode: 301
matches:
- headers:
- name: X-Forwarded-Proto
value: http
# Rule 2: API routing
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-service
port: 80
weight: 80
- name: api-service-canary
port: 80
weight: 20
Key Differences Analysis:
| Aspect | Ingress + Annotations | Gateway API |
|---|---|---|
| Type Safety | None (string key-value) | Yes (CRD schema validation) |
| IDE Support | None | Autocompletion, real-time validation |
| Deploy-time Validation | None (runtime errors) | Admission Webhook validation |
| Canary Deployment | Annotation (vendor-specific) | backendRefs.weight (standard) |
| HTTPS Redirect | Annotation (vendor-specific) | RequestRedirect filter (standard) |
| Path Rewriting | Annotation + regex | URLRewrite filter (standard) |
| RBAC Control | Cannot control per annotation | Resource-level RBAC possible |
| Audit Trail | Difficult | Possible at CRD level |
3.4 Policy Attachment: The True Alternative to Annotations
Gateway API's Policy Attachment model separates cross-cutting concerns that annotations used to handle into independent resources.
# Timeout policy: managed as a separate resource
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: BackendLBPolicy
metadata:
name: api-timeout-policy
spec:
targetRefs:
- group: ''
kind: Service
name: api-service
sessionPersistence:
sessionName: my-session
type: Cookie
---
# TLS policy: backend TLS config separated into its own resource
apiVersion: gateway.networking.k8s.io/v1alpha3
kind: BackendTLSPolicy
metadata:
name: api-backend-tls
spec:
targetRefs:
- group: ''
kind: Service
name: api-service
validation:
caCertificateRefs:
- name: backend-ca-cert
group: ''
kind: ConfigMap
hostname: api-internal.example.com
The advantages of this model are clear:
- Separation of Concerns: Routing rules (HTTPRoute) and operational policies (Policy) are managed independently
- Reusability: A single policy can be attached to multiple services
- RBAC Application: Separate RBAC rules can be applied to policy resources
- Audit Ease: Which policies are applied to which targets can be clearly tracked
4. [Takeaway 3] Separation of Roles: Peaceful Coexistence of Infrastructure Administrators and Developers
4.1 Governance Problems with Ingress
One of the fundamental design limitations of the existing Ingress API is the absence of role separation. Infrastructure settings (TLS certificates, listener ports) and application routing rules (path matching, backend services) are mixed within a single Ingress resource.
# Ingress: infrastructure and application settings mixed
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
# Who manages these annotations? Infra team? Dev team?
annotations:
nginx.ingress.kubernetes.io/ssl-redirect: 'true' # Infrastructure concern
nginx.ingress.kubernetes.io/proxy-body-size: '50m' # Infrastructure concern
nginx.ingress.kubernetes.io/rewrite-target: /$1 # Application concern
nginx.ingress.kubernetes.io/canary-weight: '10' # Application concern
spec:
ingressClassName: nginx # Infrastructure concern
tls:
- hosts:
- app.example.com
secretName: production-tls-cert # Infrastructure concern (secret management)
rules:
- host: app.example.com # Mixed concern
http:
paths:
- path: /api/(.*) # Application concern
pathType: ImplementationSpecific
backend:
service:
name: api-service # Application concern
port:
number: 80
In this structure, developers must modify the same resource that contains infrastructure settings to change their routing rules. Conversely, infrastructure administrators must access resources containing application routing rules to replace TLS certificates.
4.2 Gateway API's Role-Based Resource Hierarchy
Gateway API solves this problem with a 3-layer resource model:
┌─────────────────────────────────────────────────────────────┐
│ Layer 1: GatewayClass │
│ ───────────────────── │
│ Owner: Infrastructure Provider (Cloud vendor, Platform team)│
│ Role: Define data plane implementation │
│ Example: "nginx", "envoy", "gke-l7-global" │
│ RBAC: cluster-admin level │
├─────────────────────────────────────────────────────────────┤
│ Layer 2: Gateway │
│ ───────────────── │
│ Owner: Cluster Operator (Platform/Infra Engineer) │
│ Role: Listeners, TLS, IP addresses, ports, allowed NS │
│ Example: Port 443 HTTPS listener, wildcard certificate │
│ RBAC: namespace-admin level (infra namespace) │
├─────────────────────────────────────────────────────────────┤
│ Layer 3: HTTPRoute / GRPCRoute / TCPRoute │
│ ─────────────────────────────────────────── │
│ Owner: Application Developer (Service developer) │
│ Role: Routing rules, backend service mapping, traffic weight│
│ Example: /api → api-service, canary 20% │
│ RBAC: namespace-editor level (application namespace) │
└─────────────────────────────────────────────────────────────┘
Role Responsibility Matrix:
| Role | Resource | Scope of Responsibility | RBAC Example |
|---|---|---|---|
| Infrastructure Provider | GatewayClass | Register data plane implementation, define parameters | ClusterRole: gatewayclass-admin |
| Cluster Operator | Gateway, ReferenceGrant | Listener settings, TLS cert management, NS access control | Role: gateway-admin (infra-ns) |
| Application Developer | HTTPRoute, GRPCRoute | Routing rules, backend service mapping, traffic distribution | Role: route-editor (app-ns) |
4.3 Production RBAC Configuration Example
Step 1: Infrastructure Provider -- GatewayClass Definition
# GatewayClass defined by the infrastructure provider
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx-gateway-fabric
spec:
controllerName: gateway.nginx.org/nginx-gateway-controller
parametersRef:
group: gateway.nginx.org
kind: NginxProxy
name: production-proxy-config
Step 2: Cluster Operator -- Gateway Configuration
# Gateway managed by the cluster operator
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: gateway-infra # Infrastructure-only namespace
spec:
gatewayClassName: nginx-gateway-fabric
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls-cert
kind: Secret
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
gateway-access: 'true' # Only allowed namespaces
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Same
---
# Cross-namespace reference grant
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-gateway-to-app-secrets
namespace: app-team-a
spec:
from:
- group: gateway.networking.k8s.io
kind: Gateway
namespace: gateway-infra
to:
- group: ''
kind: Service
Step 3: Application Developer -- HTTPRoute Definition
# HTTPRoute managed by developers in their namespace
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-routes
namespace: app-team-a # Application namespace
spec:
parentRefs:
- name: production-gateway
namespace: gateway-infra # Reference Gateway in infra namespace
sectionName: https
hostnames:
- api.example.com
rules:
- matches:
- path:
type: PathPrefix
value: /v1
backendRefs:
- name: api-v1
port: 8080
weight: 80
- name: api-v2
port: 8080
weight: 20 # Canary deployment: 20% traffic to v2
RBAC Policy Example:
# Developer Role: Can only create/modify HTTPRoutes
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: route-editor
namespace: app-team-a
rules:
- apiGroups: ['gateway.networking.k8s.io']
resources: ['httproutes', 'grpcroutes']
verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']
- apiGroups: ['gateway.networking.k8s.io']
resources: ['gateways']
verbs: ['get', 'list'] # Gateway is read-only
---
# Infrastructure operator Role: Can manage Gateway, cannot create Routes
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: gateway-admin
namespace: gateway-infra
rules:
- apiGroups: ['gateway.networking.k8s.io']
resources: ['gateways', 'referencegrants']
verbs: ['get', 'list', 'watch', 'create', 'update', 'patch', 'delete']
- apiGroups: ['']
resources: ['secrets']
verbs: ['get', 'list', 'watch', 'create', 'update']
The practical value this structure provides is as follows:
- Developer Autonomy: Developers can freely change routing rules in their namespace without approval from the infrastructure team
- Infrastructure Safety: The risk of developers accidentally modifying infrastructure settings such as TLS certificates, listener ports, or allowed namespaces is fundamentally eliminated
- Cross-Namespace Security: Through
ReferenceGrant, referencing resources from other namespaces must be explicitly allowed, preventing unauthorized access - Self-Service Governance: The platform team controls which namespaces can use the Gateway via
allowedRoutes, and development teams operate freely within that scope
5. [Takeaway 4] Breaking Vendor Lock-in: Achieving Portability
5.1 The Essence of the Portability Problem
The reason the Ingress API fails to provide vendor portability is simple: the standard spec is too minimal. The Ingress API spec defines only the bare minimum: host matching, path matching, and TLS termination. All advanced features needed in practice (canary deployments, rate limiting, header manipulation, timeouts, retries, etc.) are independently implemented by each vendor through annotations.
As a result, the moment you write an Ingress resource, you are locked into a specific controller. This directly conflicts with Kubernetes' ideal of "Write once, run anywhere."
┌─────────────────────────────────────────────────────────────┐
│ Ingress API Portability Limitations │
│ │
│ Ingress spec (Standard) Annotations (Non-standard) │
│ ┌───────────────────┐ ┌──────────────────────────────┐ │
│ │ host: app.com │ │ nginx.ingress.k8s.io/... │ │
│ │ path: /api │ │ traefik.ingress.k8s.io/... │ │
│ │ tls: cert │ │ alb.ingress.k8s.io/... │ │
│ │ │ │ haproxy.org/... │ │
│ │ (Portable) │ │ (Non-portable - Vendor-locked)│ │
│ └───────────────────┘ └──────────────────────────────┘ │
│ 20% 80% │
│ Only 20% of actual The remaining 80% depends │
│ config is portable on vendor-specific annotations│
└─────────────────────────────────────────────────────────────┘
5.2 Gateway API's Portability Strategy
Gateway API achieves both portability and extensibility by layering features into 3 support levels:
| Support Level | Description | Portability | Conformance Test |
|---|---|---|---|
| Core | Essential features that all implementations must support | Fully portable | Mandatory pass |
| Extended | Portable features that not all implementations may support | Conditionally portable | Optional pass |
| Implementation-specific | Vendor-specific extension features | Not portable | Not applicable |
Core Features (Fully Portable):
# This HTTPRoute works identically on any Gateway API implementation
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: portable-route
spec:
parentRefs:
- name: my-gateway
hostnames:
- app.example.com
rules:
- matches:
- path:
type: PathPrefix # Core: path matching
value: /api
- headers: # Core: header matching
- name: X-Version
value: v2
filters:
- type: RequestHeaderModifier # Core: header manipulation
requestHeaderModifier:
add:
- name: X-Gateway
value: production
backendRefs:
- name: api-service
port: 8080
Extended Features (Portable but verify implementation support):
# Extended features: supported by most implementations but verify
rules:
- filters:
- type: RequestRedirect # Extended: HTTP redirect
requestRedirect:
scheme: https
statusCode: 301
- type: URLRewrite # Extended: URL rewriting
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /v2
backendRefs:
- name: api-v1
port: 8080
weight: 90 # Extended: traffic weighting (canary)
- name: api-v2
port: 8080
weight: 10
5.3 Conformance Test: The Guarantee of Portability
Gateway API's portability is backed not by mere promises but by a concrete verification mechanism called Conformance Tests.
Each implementation must pass a standardized test suite provided by the Gateway API project. These tests actually create Gateways and Routes, send traffic, and verify that they behave identically to the API specification.
┌─────────────────────────────────────────────────────────────┐
│ Conformance Test Process │
│ │
│ 1. Run Gateway API test suite │
│ └─> Deploy standard Gateway/HTTPRoute resources │
│ │
│ 2. Send actual traffic and verify behavior │
│ └─> Path matching, header filtering, redirects, etc. │
│ │
│ 3. Generate results report │
│ └─> Core 100%, Extended features list │
│ │
│ 4. Official certification (Conformance Profile) │
│ └─> Listed in official Gateway API implementations │
└─────────────────────────────────────────────────────────────┘
Conformance Status by Major Implementation:
| Implementation | Core Conformance | Extended Features | Notes |
|---|---|---|---|
| NGINX Gateway Fabric | Pass | HTTP routing, TLS, URL rewriting, weight-based distribution | NGINX data plane |
| Envoy Gateway | Pass | All Extended features supported | Envoy Proxy data plane |
| GKE Gateway Controller | Pass | GCP native integration | Google Cloud only |
| Istio | Pass | Service mesh integration | Istio data plane |
| Contour | Pass | Envoy-based | VMware supported |
| Traefik | Pass | Middleware integration | v3+ support |
5.4 Practical Portability Scenario: Multi-Cloud Migration
The portability guaranteed by Conformance Tests delivers real value in multi-cloud operations:
┌──────────────────┐ ┌──────────────────┐
│ AWS EKS │ │ Azure AKS │
│ │ │ │
│ GatewayClass: │ │ GatewayClass: │
│ aws-gateway │ │ azure-gateway │
│ (change impl) │ │ (change impl) │
│ │ │ │
│ Gateway: ───────┼─────┼─ Gateway: ──────┐│
│ (minimal edits) │ │ (minimal edits) ││
│ │ │ ││
│ HTTPRoute: ─────┼─────┼─ HTTPRoute: ────┘│
│ (reuse as-is) │ │ (reuse as-is) │
└──────────────────┘ └──────────────────┘
│ │
└───────┬────────────────┘
│
Same HTTPRoute manifests reused
by only changing GatewayClass
With the legacy Ingress approach, moving from AWS ALB Ingress Controller to GKE Ingress Controller required rewriting all annotations. With Gateway API, by only changing the GatewayClass, the remaining resources (Gateway, HTTPRoute) can be reused as-is within the scope of Core/Extended features.
6. [Takeaway 5] The Performance Leap: Full-Scale Adoption of HTTP/3 and QUIC
6.1 Why HTTP/3: The Structural Limitations of TCP
HTTP/2 enabled processing multiple streams simultaneously on the same TCP connection through multiplexing. However, it failed to solve the Head-of-Line (HOL) Blocking problem inherent in the TCP protocol itself.
HTTP/2 over TCP Head-of-Line Blocking:
TCP Connection
┌─────────────────────────────────────────────────┐
│ Stream 1: [Pkt1][Pkt2][ Lost ][Pkt4] │
│ Stream 2: [PktA][PktB] ← Wait [PktD] │ ← Blocked!
│ Stream 3: [PktX][PktY] ← Wait [PktZ] │ ← Blocked!
└─────────────────────────────────────────────────┘
Pkt3 lost → All streams wait
HTTP/3 over QUIC Independent Streams:
QUIC Connection
┌─────────────────────────────────────────────────┐
│ Stream 1: [Pkt1][Pkt2][ Lost ][Pkt4] │ ← Only this stream affected
│ Stream 2: [PktA][PktB][PktC] [PktD] │ ← Proceeds normally!
│ Stream 3: [PktX][PktY][PktZ] │ ← Proceeds normally!
└─────────────────────────────────────────────────┘
Pkt3 lost → Only Stream 1 affected
HTTP/3 uses the QUIC (Quick UDP Internet Connections) protocol instead of TCP. QUIC is built on top of UDP, and each stream is delivered independently. Packet loss in one stream does not affect other streams.
6.2 Key Performance Benefits of HTTP/3
| Feature | HTTP/2 (TCP) | HTTP/3 (QUIC) | Improvement |
|---|---|---|---|
| Transport Layer | TCP | UDP + QUIC | Reduced kernel dependency |
| Connection Establishment | TCP 3-way handshake + TLS handshake (2-3 RTT) | QUIC 0-RTT / 1-RTT | Up to 67% latency reduction |
| HOL Blocking | Occurs at TCP level | Isolated to stream level | Improved parallel processing |
| Connection Migration | Reconnection needed on IP/port change | Maintained via Connection ID | Mobile environment stability |
| Encryption | TLS optional | TLS 1.3 mandatory (built-in) | Default encryption guaranteed |
| Packet Loss Recovery | Affects entire connection | Independent per-stream recovery | Better lossy network performance |
6.3 HTTP/3 Support in NGINX Gateway Fabric
NGINX officially supports QUIC and HTTP/3 since v1.25.0, and HTTP/3 can be leveraged in Kubernetes environments through NGINX Gateway Fabric.
NginxProxy Configuration for HTTP/3 Activation:
apiVersion: gateway.nginx.org/v1alpha1
kind: NginxProxy
metadata:
name: http3-enabled-proxy
spec:
config:
http:
http3: true
http3MaxConcurrentStreams: 128
altSvcHeader: true # Automatically add Alt-Svc header
Gateway HTTP/3 Listener Configuration:
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: http3-gateway
namespace: gateway-infra
spec:
gatewayClassName: nginx-gateway-fabric
listeners:
# HTTPS (TCP) + HTTP/3 (UDP) simultaneous listening
- name: https-tcp
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls-cert
- name: https-udp
protocol: HTTPS # HTTP/3 declared as HTTPS protocol
port: 443 # Same port, QUIC handled over UDP
tls:
mode: Terminate
certificateRefs:
- name: wildcard-tls-cert
Exposing UDP Port in Service:
apiVersion: v1
kind: Service
metadata:
name: nginx-gateway-fabric
namespace: gateway-infra
spec:
type: LoadBalancer
ports:
- name: https-tcp
port: 443
targetPort: 443
protocol: TCP
- name: https-udp
port: 443
targetPort: 443
protocol: UDP # UDP port required for QUIC
6.4 HTTP/3 Adoption Considerations
There are technical requirements and considerations that must be verified when adopting HTTP/3:
1) SSL Library Requirements
QUIC protocol support requires a QUIC-compatible SSL library. Standard OpenSSL does not support the QUIC API (as of OpenSSL 3.x), so one of the following alternatives must be used:
| SSL Library | QUIC Support | Notes |
|---|---|---|
| BoringSSL | Native support | Developed by Google, default in NGINX Gateway Fabric |
| LibreSSL | 3.6.0+ support | OpenBSD project |
| QuicTLS | OpenSSL + QUIC patch | OpenSSL fork with QUIC API added |
| OpenSSL | Not supported (3.x) | Future support planned |
NGINX Gateway Fabric is built with BoringSSL by default, so HTTP/3 can be used without any SSL library replacement.
2) Firewall and Network Configuration
┌─────────────────────────────────────────────────────────┐
│ HTTP/3 Network Requirements │
│ │
│ UDP 443 must be added to inbound rules: │
│ │
│ Before (HTTP/2 only): │
│ ┌──────────────────────────────────────┐ │
│ │ TCP 80 ← HTTP │ │
│ │ TCP 443 ← HTTPS (TLS over TCP) │ │
│ └──────────────────────────────────────┘ │
│ │
│ After adding HTTP/3: │
│ ┌──────────────────────────────────────┐ │
│ │ TCP 80 ← HTTP │ │
│ │ TCP 443 ← HTTPS (TLS over TCP) │ │
│ │ UDP 443 ← QUIC (HTTP/3 over UDP) │ ← Must add │
│ └──────────────────────────────────────┘ │
│ │
│ Note: Many enterprise firewalls and cloud security │
│ groups block UDP 443 by default. │
└─────────────────────────────────────────────────────────┘
- Cloud Security Groups: UDP 443 inbound must be explicitly allowed in AWS Security Groups, Azure NSGs, GCP Firewall Rules
- CDN/WAF Compatibility: Some CDNs or WAFs may block or improperly handle QUIC traffic
- Load Balancer Support: Verify that cloud load balancers correctly route UDP traffic. AWS NLB, Azure Load Balancer, and GCP Network Load Balancer support UDP
3) Gradual Transition via Alt-Svc Header
HTTP/3 adoption can proceed gradually. The server includes an Alt-Svc header in HTTP/2 responses to inform clients that HTTP/3 is available:
Alt-Svc: h3=":443"; ma=86400
After receiving this header, the client attempts HTTP/3 on subsequent requests. If it fails, it automatically falls back to HTTP/2. As of 2025, Chrome 87+, Firefox 88+, Safari 14+, and more than 95% of all browsers support HTTP/3.
4) QUIC Connection Migration with eBPF
Linux 5.7+ kernels support QUIC packet routing using eBPF. This enables QUIC's Connection Migration feature, allowing connections to persist even when the client's network changes (e.g., switching from Wi-Fi to LTE).
# Enable eBPF-based QUIC routing in nginx.conf
quic_bpf on; # Requires Linux 5.7+
7. Practical Migration Guide
7.1 Migration Strategy Overview
Migration from Ingress NGINX to Gateway API should follow a Progressive Migration approach rather than a Big Bang approach.
┌──────────────────────────────────────────────────────────────┐
│ Progressive Migration Strategy │
│ │
│ Phase 1: Inventory and Assessment (1-2 weeks) │
│ ├─ Catalog all Ingress resources │
│ ├─ Classify annotations in use │
│ ├─ Identify snippets usage (priority: security risk removal)│
│ └─ Map dependencies (cert-manager, external-dns, etc.) │
│ │
│ Phase 2: Build Gateway API Infrastructure (1-2 weeks) │
│ ├─ Install Gateway API CRDs │
│ ├─ Deploy NGINX Gateway Fabric or alternative controller │
│ ├─ Create GatewayClass, Gateway resources │
│ └─ Validate in non-production environment │
│ │
│ Phase 3: Progressive Routing Transition (2-4 weeks) │
│ ├─ Initial conversion with ingress2gateway tool │
│ ├─ Manual review & annotation → filter/policy conversion │
│ ├─ Sequential per-service transition (non-critical → critical)│
│ └─ Parallel operation period (Ingress + Gateway API) │
│ │
│ Phase 4: Full Transition and Cleanup (1-2 weeks) │
│ ├─ Confirm all traffic routes through Gateway API │
│ ├─ Remove Ingress resources │
│ ├─ Remove ingress-nginx controller │
│ └─ Reconfigure monitoring and alerts │
└──────────────────────────────────────────────────────────────┘
7.2 Phase 1: Inventory Collection
Query all Ingress resources:
# List all Ingress resources across all namespaces
kubectl get ingress -A -o wide
# Extract all annotations in use
kubectl get ingress -A -o json | \
jq -r '.items[].metadata.annotations // {} | keys[]' | \
sort | uniq -c | sort -rn
# Urgent check for snippets annotations (security risk)
kubectl get ingress -A -o json | \
jq -r '.items[] | select(.metadata.annotations["nginx.ingress.kubernetes.io/configuration-snippet"] != null or .metadata.annotations["nginx.ingress.kubernetes.io/server-snippet"] != null) | .metadata.namespace + "/" + .metadata.name'
# Check IngressClass
kubectl get ingressclass
Annotation Classification Checklist:
| Classification | Annotation Examples | Gateway API Replacement | Urgency |
|---|---|---|---|
| Security Risk | configuration-snippet, server-snippet | Remove or Custom Filter | Immediate |
| Standard Replaceable | ssl-redirect, rewrite-target | RequestRedirect, URLRewrite filter | High |
| Policy Replaceable | proxy-read-timeout, proxy-body-size | Policy Attachment | Medium |
| Feature Replaceable | canary, canary-weight | backendRefs.weight | Medium |
| Implementation Dependent | upstream-hash-by, affinity | Check implementation-specific CRDs | Low |
7.3 Phase 2: Using the ingress2gateway Tool
ingress2gateway is an official migration tool provided by Kubernetes SIG-Network.
Installation:
# With Go environment
go install github.com/kubernetes-sigs/ingress2gateway@latest
# Or download binary from GitHub Releases
# https://github.com/kubernetes-sigs/ingress2gateway/releases
Basic Usage:
# Auto-convert Ingress resources from the cluster
ingress2gateway print --all-namespaces
# Convert specific namespace only
ingress2gateway print --namespace production
# File-based conversion
ingress2gateway print --input-file ingress-resources.yaml
# Specify ingress-nginx provider (annotation conversion support)
ingress2gateway print --providers ingress-nginx --all-namespaces
# Save results to file
ingress2gateway print --providers ingress-nginx --all-namespaces > gateway-resources.yaml
Important Note: ingress2gateway is only a starting point and does not perfectly convert all annotations. Implementation-specific features (snippets, custom Lua scripts, etc.) must be manually redesigned. Always review conversion results and thoroughly test in a non-production environment before applying.
7.4 Phase 3: Deploying NGINX Gateway Fabric
Installation via Helm:
# Install Gateway API CRDs
kubectl kustomize \
"https://github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.1" \
| kubectl apply -f -
# Install NGINX Gateway Fabric
helm install ngf oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
--create-namespace \
--namespace nginx-gateway \
--set service.type=LoadBalancer
Verify GatewayClass:
# Check GatewayClass after installation
kubectl get gatewayclass
# NAME CONTROLLER ACCEPTED
# nginx gateway.nginx.org/nginx-gateway-controller True
Create and Validate Gateway:
# Apply Gateway resource
kubectl apply -f gateway.yaml
# Check Gateway status
kubectl get gateway -n gateway-infra
# NAME CLASS ADDRESS PROGRAMMED AGE
# production-gateway nginx 203.0.113.10 True 5m
# Apply HTTPRoute
kubectl apply -f httproute.yaml
# Check HTTPRoute status
kubectl get httproute -n app-team-a
# NAME HOSTNAMES AGE
# api-routes ["api.example.com"] 2m
# Test with actual traffic
curl -H "Host: api.example.com" https://203.0.113.10/v1/health
7.5 Ingress vs Gateway API Feature Comparison Summary
| Feature | Ingress API | Gateway API | Notes |
|---|---|---|---|
| Host-based Routing | spec.rules[].host | spec.hostnames | Equivalent |
| Path-based Routing | spec.rules[].http.paths | spec.rules[].matches[].path | Gateway API more flexible |
| TLS Termination | spec.tls | Gateway.listeners[].tls | Role separation (managed at Gateway) |
| HTTPS Redirect | Annotation (vendor-specific) | RequestRedirect filter (standard) | Gateway API advantage |
| URL Rewriting | Annotation (vendor-specific) | URLRewrite filter (standard) | Gateway API advantage |
| Canary Deployment | Annotation (vendor-specific) | backendRefs.weight (standard) | Gateway API advantage |
| Header Matching | Not supported (annotation only) | spec.rules[].matches[].headers | Gateway API advantage |
| Header Manipulation | Annotation (vendor-specific) | RequestHeaderModifier filter | Gateway API advantage |
| Traffic Mirroring | Annotation (vendor-specific) | RequestMirror filter (Extended) | Gateway API advantage |
| Timeouts | Annotation (vendor-specific) | HTTPRoute timeouts (v1.2+) | Gateway API advantage |
| Retries | Annotation (vendor-specific) | HTTPRoute retry (v1.2+) | Gateway API advantage |
| gRPC Routing | Not supported (annotation only) | GRPCRoute (GA) | Gateway API advantage |
| TCP/UDP Routing | Not supported | TCPRoute, UDPRoute (Experimental) | Gateway API advantage |
| Role-based Separation | Not supported | GatewayClass/Gateway/Route separation | Gateway API advantage |
| Cross-Namespace | Not supported | ReferenceGrant | Gateway API advantage |
| Vendor Portability | Low (annotation-dependent) | High (Conformance Test) | Gateway API advantage |
| Backend TLS | Annotation (vendor-specific) | BackendTLSPolicy (v1.4 GA) | Gateway API advantage |
| WebSocket | Annotation (vendor-specific) | Standard support (v1.2+) | Gateway API advantage |
8. Comprehensive Migration Checklist
Below is a comprehensive checklist for transitioning from Ingress NGINX to Gateway API. Adapt it to your organization's situation.
8.1 Assessment Phase
- Complete inventory of all Ingress resources and namespace mapping across all clusters
- Complete audit of all Ingress NGINX annotations in use
- Confirm
configuration-snippet/server-snippetusage and assess security risk - Verify ingress-nginx controller version (CVE-2025-1974 patch status)
- Identify dependencies on integrated systems (cert-manager, external-dns, etc.)
- Document current traffic patterns and routing rules
8.2 Planning Phase
- Select target Gateway API implementation (NGINX Gateway Fabric, Envoy Gateway, etc.)
- Establish GatewayClass/Gateway/Route role separation policy
- Design namespace strategy (infrastructure vs. application)
- Design RBAC policy (infrastructure team vs. development team privilege separation)
- Determine migration order (non-critical services first)
- Establish rollback plan
- Decide on HTTP/3 adoption
8.3 Execution Phase
- Install Gateway API CRDs (
gateway.networking.k8s.io) - Deploy Gateway API implementation controller
- Create GatewayClass resource and verify status (
ACCEPTED: True) - Create Gateway resource and confirm IP address allocation (
PROGRAMMED: True) - Run initial conversion with
ingress2gatewaytool - Manually review conversion results and supplement missing features
- Test converted HTTPRoutes in non-production environment
- Gradually shift traffic using DNS weight-based routing
- Production environment transition and monitoring
8.4 Validation Phase
- Confirm normal responses for all routing paths
- Verify TLS certificate termination and renewal work correctly
- Confirm canary/weight-based deployment behavior
- Verify header-based routing behavior
- Confirm error pages and default backend behavior
- Verify monitoring and alerting work correctly
- Perform load testing and compare against performance baseline
8.5 Cleanup Phase
- Remove legacy Ingress resources
- Remove ingress-nginx controller Deployment/DaemonSet
- Clean up ingress-nginx related ConfigMaps, ServiceAccounts, RBAC resources
- Remove ingress-nginx Service (LoadBalancer) and reclaim IP addresses
- Clean up Helm releases (if applicable)
- Remove or replace Ingress-related manifests in CI/CD pipelines with Gateway API
- Remove ingress-nginx modules from IaC (Terraform, Pulumi, etc.)
- Team training and operational documentation update
9. Conclusion: It Is Time to Act
The retirement of Ingress NGINX is not simply an event where one controller disappears. It is a turning point for the entire Kubernetes networking paradigm.
Summary: 5 Key Changes and Response Directions
| # | Change | Key Message | Immediate Action |
|---|---|---|---|
| 1 | Ingress NGINX EOL | No security patches after March 2026 | Begin migration planning |
| 2 | Escape Annotations | Structural flaws directly lead to vulnerabilities | Remove snippets usage immediately |
| 3 | Role Separation | GatewayClass/Gateway/Route 3-layer model | Redesign RBAC policies |
| 4 | Vendor Portability | Conformance Test-based standardization | Revisit multi-cloud strategy |
| 5 | HTTP/3 & QUIC | Next-gen protocol based on UDP | Review firewall UDP 443 opening |
3 Actions to Execute Right Now:
1. Collect Inventory (Today)
# Execute this right now
kubectl get ingress -A -o json | \
jq -r '[.items[] | {
namespace: .metadata.namespace,
name: .metadata.name,
annotations: (.metadata.annotations // {} | keys | length),
has_snippets: ((.metadata.annotations // {}) |
has("nginx.ingress.kubernetes.io/configuration-snippet") or
has("nginx.ingress.kubernetes.io/server-snippet"))
}]'
2. Run ingress2gateway Tool (This Week)
go install github.com/kubernetes-sigs/ingress2gateway@latest
ingress2gateway print --providers ingress-nginx -A > gateway-migration.yaml
3. Build NGINX Gateway Fabric Test Environment (This Month)
# Test on a non-production cluster
kubectl kustomize \
"https://github.com/kubernetes-sigs/gateway-api/config/crd?ref=v1.2.1" \
| kubectl apply -f -
helm install ngf oci://ghcr.io/nginx/charts/nginx-gateway-fabric \
--create-namespace --namespace nginx-gateway
The experience and lessons the Kubernetes community has built over a decade are distilled into Gateway API as the new standard. It is time to move from the annotation swamp to type-safe APIs, from mixed single resources to role-based separation, and from vendor lock-in to the freedom of portability.
The March 2026 deadline is not a threat but an opportunity. Through this transition, you can build more secure, more flexible, and more standardized networking infrastructure. Start now.
10. References
Official Documentation and Announcements
- Ingress NGINX Retirement: What You Need to Know | Kubernetes Blog
- Gateway API v1.2: WebSockets, Timeouts, Retries, and More | Kubernetes Blog
- Gateway API v1.4: New Features | Kubernetes Blog
- Gateway API Official Documentation
- Gateway API Conformance
- Ingress-nginx CVE-2025-1974: What You Need to Know | Kubernetes Blog
NGINX Gateway Fabric
- F5 NGINX Gateway Fabric Documentation
- NGINX Gateway Fabric GitHub
- What's New in F5 NGINX Gateway Fabric 2.3.0
- Gateway Architecture | NGINX Documentation
- Gateway API Compatibility | NGINX Documentation
Security and CVE
- IngressNightmare: CVE-2025-1974 | Wiz Blog
- Detecting and Mitigating IngressNightmare | Sysdig
- IngressNightmare CVE-2025-1974 | FortiGuard Labs
Migration Guides
- Migrating from Ingress | Gateway API Documentation
- Migrating from Ingress-NGINX | Gateway API Documentation
- ingress2gateway GitHub
- Introducing ingress2gateway | Kubernetes Blog
Cloud Vendors
- AKS Application Routing Add-on Ingress-NGINX Update | AKS Engineering Blog
- The End of an Era: Transitioning Away from Ingress NGINX | Google Open Source Blog