Introduction
After years as the de facto standard for HTTP routing in Kubernetes, Ingress is being retired. The Ingress-NGINX project announced in March 2026 that it’s entering maintenance mode, and the Kubernetes community has thrown its weight behind the Gateway API as the future of traffic management.
This isn’t just a rename. Gateway API represents a fundamental rethinking of how Kubernetes handles ingress traffic—more expressive, more secure, and designed for the multi-team, multi-tenant reality of modern platform engineering. But migration isn’t trivial: years of accumulated annotations, controller-specific configurations, and tribal knowledge need to be carefully translated.
This article covers why the migration is happening, how Gateway API differs architecturally, and provides a practical migration workflow using the new Ingress2Gateway tool that reached 1.0 in March 2026.
Why Ingress Is Being Retired
Ingress served Kubernetes well for nearly a decade, but its limitations have become increasingly painful:
The Annotation Problem
Ingress’s core specification is minimal—it handles basic host and path routing. Everything else—rate limiting, authentication, header manipulation, timeouts, body size limits—lives in annotations. And annotations are controller-specific.
# NGINX-specific annotations
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-app
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: "50m"
nginx.ingress.kubernetes.io/proxy-read-timeout: "60"
nginx.ingress.kubernetes.io/ssl-redirect: "true"
nginx.ingress.kubernetes.io/auth-url: "https://auth.example.com/verify"
# ... dozens more
Switch from NGINX to Traefik? Rewrite all your annotations. Want to use multiple ingress controllers? Good luck keeping the annotation schemas straight. This has led to:
- Vendor lock-in: Teams hesitate to switch controllers because migration costs are high
- Configuration sprawl: Critical routing logic is buried in annotations that are hard to audit
- No validation: Annotations are strings—typos cause runtime failures, not deployment rejections
The RBAC Gap
Ingress is a single resource type. If you can edit an Ingress, you can edit any Ingress in that namespace. There’s no built-in way to separate „who can define routes“ from „who can configure TLS“ from „who can set up authentication policies.“
In multi-team environments, this forces platform teams to either:
- Give app teams too much power (security risk)
- Centralize all Ingress management (bottleneck)
- Build custom admission controllers (complexity)
Limited Expressiveness
Modern traffic management needs capabilities that Ingress simply doesn’t support natively:
- Traffic splitting for canary deployments
- Header-based routing
- Request/response transformation
- Cross-namespace routing
- TCP/UDP routing (not just HTTP)
Enter Gateway API
Gateway API is designed from the ground up to address these limitations. It’s not just „Ingress v2″—it’s a complete reimagining of how Kubernetes handles traffic.
Resource Model
Instead of cramming everything into one resource, Gateway API separates concerns:
┌─────────────────────────────────────────────────────────────┐
│ GATEWAY API MODEL │
│ │
│ ┌─────────────────┐ │
│ │ GatewayClass │ ← Infrastructure provider config │
│ │ (cluster-wide) │ (managed by platform team) │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ Gateway │ ← Deployment of load balancer │
│ │ (namespace) │ (managed by platform team) │
│ └────────┬────────┘ │
│ │ │
│ ┌────────▼────────┐ │
│ │ HTTPRoute │ ← Routing rules │
│ │ (namespace) │ (managed by app teams) │
│ └─────────────────┘ │
└─────────────────────────────────────────────────────────────┘
- GatewayClass: Defines the controller implementation (like IngressClass, but richer)
- Gateway: Represents an actual load balancer deployment with listeners
- HTTPRoute: Defines routing rules that attach to Gateways
- Plus: TCPRoute, UDPRoute, GRPCRoute, TLSRoute for non-HTTP traffic
RBAC-Native Design
Each resource type has separate RBAC controls:
# Platform team: can manage GatewayClass and Gateway
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: gateway-admin
rules:
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["gatewayclasses", "gateways"]
verbs: ["*"]
---
# App team: can only manage HTTPRoutes in their namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: route-admin
namespace: team-alpha
rules:
- apiGroups: ["gateway.networking.k8s.io"]
resources: ["httproutes"]
verbs: ["*"]
App teams can define their routing rules without touching infrastructure configuration. Platform teams control the Gateway without micromanaging every route.
Typed Configuration
No more annotation strings. Gateway API uses structured, validated fields:
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: my-app
namespace: production
spec:
parentRefs:
- name: production-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: api-service
port: 8080
weight: 90
- name: api-service-canary
port: 8080
weight: 10
timeouts:
request: 30s
filters:
- type: RequestHeaderModifier
requestHeaderModifier:
add:
- name: X-Request-ID
value: "${request_id}"
Traffic splitting, timeouts, header modification—all first-class, validated fields. No more hoping you spelled the annotation correctly.
Ingress2Gateway: The Migration Tool
The Kubernetes SIG-Network team released Ingress2Gateway 1.0 in March 2026, providing automated translation of Ingress resources to Gateway API equivalents.
Installation
# Install via Go
go install github.com/kubernetes-sigs/ingress2gateway@latest
# Or download binary
curl -LO https://github.com/kubernetes-sigs/ingress2gateway/releases/latest/download/ingress2gateway-linux-amd64
chmod +x ingress2gateway-linux-amd64
sudo mv ingress2gateway-linux-amd64 /usr/local/bin/ingress2gateway
Basic Usage
# Convert a single Ingress
ingress2gateway print --input-file ingress.yaml
# Convert all Ingresses in a namespace
kubectl get ingress -n production -o yaml | ingress2gateway print
# Convert and apply directly
kubectl get ingress -n production -o yaml | ingress2gateway print | kubectl apply -f -
What Gets Translated
Ingress2Gateway handles:
- Host and path rules: Direct translation to HTTPRoute
- TLS configuration: Mapped to Gateway listeners
- Backend services: Converted to backendRefs
- Common annotations: Timeout, body size, redirects → native fields
What Requires Manual Work
Not everything translates automatically:
- Controller-specific annotations: Authentication plugins, custom Lua scripts, rate limiting configurations often need manual migration
- Complex rewrites: Regex-based path rewrites may need adjustment
- Custom error pages: Implementation varies by Gateway controller
Ingress2Gateway generates warnings for annotations it can’t translate, giving you a checklist for manual review.
Migration Workflow
Phase 1: Assessment
# Inventory all Ingresses
kubectl get ingress -A -o yaml > all-ingresses.yaml
# Run Ingress2Gateway in analysis mode
ingress2gateway print --input-file all-ingresses.yaml 2>&1 | tee migration-report.txt
# Review warnings for untranslatable annotations
grep "WARNING" migration-report.txt
Phase 2: Parallel Deployment
Don’t cut over immediately. Run both Ingress and Gateway API in parallel:
# Deploy Gateway controller (e.g., Envoy Gateway, Cilium, NGINX Gateway Fabric)
helm install envoy-gateway oci://docker.io/envoyproxy/gateway-helm --version v1.0.0 -n envoy-gateway-system --create-namespace
# Create GatewayClass
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: envoy
spec:
controllerName: gateway.envoyproxy.io/gatewayclass-controller
# Create Gateway (gets its own IP/hostname)
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production
namespace: gateway-system
spec:
gatewayClassName: envoy
listeners:
- name: https
protocol: HTTPS
port: 443
tls:
mode: Terminate
certificateRefs:
- name: wildcard-cert
Phase 3: Traffic Shift
With both systems running, gradually shift traffic:
- Update DNS to point to Gateway API endpoint with low weight
- Monitor error rates, latency, and functionality
- Increase Gateway API traffic percentage
- Once at 100%, remove old Ingress resources
Phase 4: Testing
Behavioral equivalence testing is critical:
# Compare responses between Ingress and Gateway
for endpoint in $(cat endpoints.txt); do
ingress_response=$(curl -s "https://ingress.example.com$endpoint")
gateway_response=$(curl -s "https://gateway.example.com$endpoint")
if [ "$ingress_response" != "$gateway_response" ]; then
echo "MISMATCH: $endpoint"
fi
done
Common Migration Pitfalls
Default Timeout Differences
Ingress-NGINX defaults to 60-second timeouts. Some Gateway implementations default to 15 seconds. Explicitly set timeouts to avoid surprises:
rules:
- matches:
- path:
value: /api
timeouts:
request: 60s
backendRequest: 60s
Body Size Limits
NGINX’s proxy-body-size annotation doesn’t have a direct equivalent in all Gateway implementations. Check your controller’s documentation for request size configuration.
Cross-Namespace References
Gateway API supports cross-namespace routing, but it requires explicit ReferenceGrant resources:
# Allow HTTPRoutes in team-alpha to reference services in backend namespace
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-team-alpha
namespace: backend
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: team-alpha
to:
- group: ""
kind: Service
Service Mesh Interaction
If you’re running Istio or Cilium, check their Gateway API support status. Both now implement Gateway API natively, which can simplify your stack—but migration needs coordination.
Gateway Controller Options
Several controllers implement Gateway API:
| Controller | Backing Proxy | Notes |
|---|---|---|
| Envoy Gateway | Envoy | CNCF project, feature-rich |
| NGINX Gateway Fabric | NGINX | From F5/NGINX team |
| Cilium | Envoy (eBPF) | If already using Cilium CNI |
| Istio | Envoy | Native Gateway API support |
| Traefik | Traefik | Good for existing Traefik users |
| Kong | Kong | Enterprise features available |
Timeline and Urgency
While Ingress isn’t disappearing overnight, the writing is on the wall:
- March 2026: Ingress-NGINX enters maintenance mode
- Gateway API v1.0: Already stable since late 2023
- New features: Only coming to Gateway API (traffic splitting, GRPC routing, etc.)
Start planning migration now. Even if you don’t execute immediately, understanding Gateway API will be essential for any new Kubernetes work.
Conclusion
The migration from Ingress to Gateway API is inevitable, but it doesn’t have to be painful. Gateway API offers genuine improvements—better RBAC, typed configuration, richer routing capabilities—that justify the migration effort.
Start with Ingress2Gateway to understand the scope of your migration. Deploy Gateway API alongside Ingress to validate behavior. Shift traffic gradually, test thoroughly, and you’ll emerge with a more maintainable, more secure traffic management layer.
The annotation chaos era is ending. The future of Kubernetes traffic management is typed, validated, and RBAC-native. It’s time to migrate.
