Kubernetes Ingress to Gateway API Migration: How to Move Without Breaking Production

To migrate ingress to Gateway API without breaking production, the work starts before you touch a manifest — in the annotation audit, the ConfigMap defaults, and the shared Ingress resources that no migration tool can handle for you.
Most migrations don’t fail during the cutover. They fail in the translation layer — quietly, before traffic ever moves. The annotation audit skipped. The ingress2gateway output treated as deployment-ready. The staging environment that shared none of the complexity of production. By the time the failure surfaces, it looks like a Gateway API problem. It isn’t. It’s a migration preparation problem.
Part 0 covered the four paths and their failure identities. Part 1 covered why the ecosystem shift is real and what annotation sprawl costs over time. The controller decision post covered which implementation fits which environment. This post is the execution layer.
Ingress-NGINX hit EOL on March 24 — the repository is read-only, no patches, no CVE fixes. Kubernetes 1.36 drops April 22 with Gateway API as the centerpiece. The window where this was a future consideration closed.

Before You Migrate Ingress to Gateway API — The Annotation Audit
The annotation count per Ingress resource is the number that determines which migration path is actually viable. Run this before anything else:

bash
# Count annotations per ingress resource across all namespaces
kubectl get ingress -A -o json | \
jq -r '.items[] | "\(.metadata.namespace)/\(.metadata.name): \(.metadata.annotations | length) annotations"' | \
sort -t: -k2 -rn
bash
# Find ingress resources routing multiple hosts — shared ingress candidates
kubectl get ingress -A -o json | \
jq -r '.items[] | select(.spec.rules | length > 5) |
"\(.metadata.namespace)/\(.metadata.name): \(.spec.rules | length) host rules"'
ingress2gateway 1.0 — Syntax Translator, Not Architecture Translator
ingress2gateway 1.0 is a significant improvement over previous versions. It supports 30+ common Ingress-NGINX annotations with behavioral equivalence tests that verify the translated configuration matches runtime behavior in live clusters — not just YAML structure. For straightforward environments it is genuinely useful.
It is a syntax translator. It is not an architecture translator.
What ingress2gateway translates cleanly:
bash
# Run ingress2gateway against your cluster
ingress2gateway print \
--providers=ingress-nginx \
--namespace=production
- > Host and path routing rules
- > TLS referencing existing Secrets
- > CORS headers
- > Backend TLS protocol
- > Path rewrites and regex matching
- > configuration-snippet (custom Lua)
- > server-snippet (server-level config)
- > auth-url / auth-signin
- > ConfigMap global defaults
- > Implicit proxy/timeout behavior
Implicit defaults that disappear — Ingress-NGINX applies defaults globally that you may not know exist: proxy buffer sizes, upstream keepalive connections, timeout values. These are not in your Ingress manifests. They are in your ConfigMap. They do not transfer to Gateway API automatically. Document your ConfigMap before migration and verify whether those defaults need to be explicitly set in your Gateway implementation.
What to Migrate First — and What to Leave Alone
Migration sequence matters more than migration speed. The temptation is to start with the highest-traffic services to prove the pattern works. That is the wrong sequence.
The Side-by-Side Deployment Pattern — The Only Safe Model

Cutover-first is an anti-pattern. Deploying Gateway API and decommissioning Ingress in the same change window is how teams end up rolling back at 2AM.
The side-by-side deployment pattern runs both controllers simultaneously against the same cluster, sharing the same external load balancer IP. Traffic routes through Ingress for existing services. New and migrated services route through Gateway API. Neither controller touches the other’s resources.
bash
# Install Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml
# Deploy your chosen Gateway API controller alongside existing Ingress controller
# Example: NGINX Gateway Fabric
kubectl apply -f https://github.com/nginx/nginx-gateway-fabric/releases/download/v1.5.0/nginx-gateway-fabric.yaml
# Verify both controllers are running independently
kubectl get pods -n ingress-nginx
kubectl get pods -n nginx-gateway
Deploy a GatewayClass and Gateway resource pointing to your existing load balancer:
yaml
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
name: nginx-gateway
spec:
controllerName: gateway.nginx.org/nginx-gateway-controller
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
namespace: nginx-gateway
spec:
gatewayClassName: nginx-gateway
listeners:
- name: http
port: 80
protocol: HTTP
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: production-tls
namespace: nginx-gateway
Start routing new services through HTTPRoutes. Do not touch existing Ingress resources. Validate each migrated service under load before decommissioning its Ingress equivalent. The DNS cutover — updating the external record from the Ingress controller service to the Gateway controller service — happens last, after all services have been validated in the side-by-side state.
The only caveat: do not configure both an Ingress resource and an HTTPRoute for the same hostname and path simultaneously. The two controllers would compete for the same traffic. Migrate one or the other — never both active for the same route.
HTTPRoute Translation Reality — What Maps Clean, What Doesn’t
The basic HTTPRoute is straightforward for engineers coming from Ingress:
yaml
# Ingress equivalent
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: app-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: app.example.com
http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: api-service
port:
number: 8080
---
# Gateway API equivalent
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: app-route
namespace: production
spec:
parentRefs:
- name: production-gateway
namespace: nginx-gateway
hostnames:
- "app.example.com"
rules:
- matches:
- path:
type: PathPrefix
value: /api
filters:
- type: URLRewrite
urlRewrite:
path:
type: ReplacePrefixMatch
replacePrefixMatch: /
backendRefs:
- name: api-service
port: 8080
Traffic splitting is where Gateway API’s model is genuinely superior — what required custom annotations in Ingress is native in HTTPRoute:
yaml
# Canary deployment — native weight-based splitting
rules:
- backendRefs:
- name: api-stable
port: 8080
weight: 90
- name: api-canary
port: 8080
weight: 10
Adjacent Dependencies — TLS, cert-manager, ExternalDNS
Three ecosystem tools break first when you migrate. Address them before the first HTTPRoute goes live.
yaml
# cert-manager Gateway API integration
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: production-gateway
annotations:
cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
listeners:
- name: https
port: 443
protocol: HTTPS
tls:
mode: Terminate
certificateRefs:
- name: production-tls
The DNS Cutover — Zero-Downtime Execution
The shared-IP strategy makes the DNS cutover the safest step in the migration, not the riskiest. Because both controllers are running against the same load balancer IP during the side-by-side phase, the DNS record change is effectively a no-op for traffic that has already been migrated to HTTPRoutes.
Production Failure Modes — Works in Staging, Breaks in Production
Staging environments don’t have the annotation complexity, traffic volume, or cross-namespace dependency graph of production. These failure modes are invisible until they aren’t.

frontend referencing a Service in namespace api requires a ReferenceGrant in the api namespace. Without it the route fails silently — accepted status, 500 response.The ReferenceGrant fix — required for any cross-namespace HTTPRoute:
yaml
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
name: allow-frontend-routes
namespace: api
spec:
from:
- group: gateway.networking.k8s.io
kind: HTTPRoute
namespace: frontend
to:
- group: ""
kind: Service
Multi-Cluster Considerations
Gateway API’s typed resource model makes multi-cluster standardization significantly cleaner than Ingress — but only if GatewayClass and Gateway configuration is consistent across clusters.
Define a standard GatewayClass per environment tier and manage it through the same GitOps pipeline that manages cluster provisioning. HTTPRoute resources then become portable. The annotation sprawl that accumulated in Ingress configs over three years will accumulate in Gateway configurations over the next three years if there is no governance layer enforcing consistency.
The Kubernetes Cluster Orchestration guide covers the cluster fleet management model that keeps this consistent at scale. For AI infrastructure workloads with deterministic networking requirements, the deterministic networking for AI infrastructure post covers where Gateway API’s traffic management model intersects with inference workload latency requirements.
Kubernetes 1.36 — What Changes April 22
Architect’s Verdict
The Gateway API migration is not complicated. It is detailed — and the details are where teams get into trouble.
ingress2gateway 1.0 is a genuine improvement and it handles the majority of straightforward migrations cleanly. The gap it cannot close is between syntax translation and architectural translation. Annotations that encode behavior — external auth flows, custom Lua, upstream connection tuning — require architectural decisions that no tool can make for you. Find them during the audit, not during the rollback.
The side-by-side pattern is not a conservative approach. It is the correct one. Both controllers running against the same load balancer IP costs nothing in complexity and eliminates the primary risk vector of Gateway API migrations: the all-at-once cutover that discovers production failure modes under incident conditions.
Migrate simple services first. Let the pattern prove itself before it carries your annotation-heavy services. Run the annotation audit before you touch a manifest. Address cert-manager, ExternalDNS, and your ConfigMap defaults before the first HTTPRoute goes live. Keep your Ingress resources around for 24 hours after the DNS cutover.
The migration doesn’t fail where you think it will. It fails in everything you assumed would just translate.
Additional Resources
Editorial Integrity & Security Protocol
This technical deep-dive adheres to the Rack2Cloud Deterministic Integrity Standard. All benchmarks and security audits are derived from zero-trust validation protocols within our isolated lab environments. No vendor influence.
Get the Playbooks Vendors Won’t Publish
Field-tested blueprints for migration, HCI, sovereign infrastructure, and AI architecture. Real failure-mode analysis. No marketing filler. Delivered weekly.
Select your infrastructure paths. Receive field-tested blueprints direct to your inbox.
- > Virtualization & Migration Physics
- > Cloud Strategy & Egress Math
- > Data Protection & RTO Reality
- > AI Infrastructure & GPU Fabric
Zero spam. Includes The Dispatch weekly drop.
Need Architectural Guidance?
Unbiased infrastructure audit for your migration, cloud strategy, or HCI transition.
>_ Request Triage Session