Imagine moving a shop to a new building on the same street. You do not close the old shop, empty it and hope customers find the new one. You set up the new shop completely, test it with staff, then change the sign at the end of the street to point to the new address, and keep the old one ready for a few days in case something was forgotten.
Moving from Ingress to Gateway API works the same way. ingress2gateway reads your Ingresses and prints Gateway and HTTPRoute objects (with warnings on stderr for what it cannot translate), you review and apply them next to the old controller, the new Gateway gets its own address, you test it there, and the cutover is one DNS change, which can be reverted.
Why now, and the plan
With ingress-nginx retired (no security fixes since March 2026), every team running it has the same project: move to a Gateway API implementation without an outage. The API move is the easy part; the annotations are the work.
What you need to know already: the ingress-nginx lessons of this chapter (annotations, canaries, rewrites), the two Gateway API lessons before this one, external-dns, cert-manager.
The map
ingress-nginx annotation Gateway API
host + path rules HTTPRoute hostnames + matches
pathType Exact / Prefix path type Exact / PathPrefix
use-regex path type RegularExpression (implementation-specific)
rewrite-target: /new URLRewrite ReplacePrefixMatch / ReplaceFullPath
rewrite-target: /$2 (no equivalent - capture groups)
ssl-redirect (308) RequestRedirect on the port-80 listener
canary + canary-weight weights on backendRefs
canary-by-header a rule with a headers match
proxy-read-timeout rules[].timeouts.request
backend-protocol: HTTPS BackendTLSPolicy (standard since 1.4)
cert-manager.io/cluster-issuer the same annotation on the Gateway
allowlist-source-range, auth-url, implementation policies (Envoy Gateway SecurityPolicy,
limit-rps, affinity, snippets Istio AuthorizationPolicy, ...) or nothing
ingress2gateway
The Kubernetes project's converter (1.0 in March 2026; v1.2 in the lab). It reads Ingresses from the cluster or from files and prints Gateway API objects; it never applies anything:
It writes one Gateway per Ingress class (gatewayClassName = the class name: edit it to your implementation's class) and one HTTPRoute per host, and it merges canary Ingresses into weights. Everything it cannot translate goes to stderr as a warning box:
┌─ WARN ────────────────────────────────────────
│ IP-based authorization is not supported
│ source: INGRESS-NGINX
│ object: Ingress: shop/admin
└─
Each WARN is a feature you would silently lose. --emitter envoy-gateway (or kgateway, agentgateway) adds that implementation's extension objects for some of them.
Side by side, then DNS
Never convert in place. The safe sequence:
Install the Gateway API implementation next to ingress-nginx. Its Gateway gets its own address.
Apply the generated (and reviewed) Gateway and routes. Nothing changes for users: DNS still points at ingress-nginx.
Test the new path with the right Host header against the Gateway's address (curl --resolve shop.lab:80:<gateway-ip>), including TLS, every path, the canary weights.
Move DNS: the record now points at the Gateway (external-dns with --source=gateway-httproute, or by hand). Keep TTLs low beforehand.
Watch both data planes' logs until the old one sees no traffic for a few TTLs. Rollback until then = put DNS back.
Delete the Ingresses. Last, ingress-nginx itself.
TLS: put cert-manager.io/cluster-issuer on the Gateway (cert-manager's gateway-shim) or reuse the existing Secrets with certificateRefs - the same certificate can be served by both during the overlap.
What you can now do:
map each ingress-nginx annotation to a Gateway API field or policy, or know it has none
run ingress2gateway and turn its warnings into a to-do list
run the old and new edge side by side and cut over with DNS, with a rollback
Why it helps
With ingress-nginx retired, this migration is on many teams' roadmaps, and doing it carelessly is how you cause an outage for every app at once. The safe pattern is the same everywhere: translate, review, run side by side, move one host at a time by DNS, keep the rollback.
The tool does the boring part, but the interesting part is what does not translate: snippets, some authentication annotations, controller-specific behaviour such as default timeouts. Knowing the map from annotations to Gateway API fields, and reading the warnings, is what lets you say in a design review exactly which behaviour changes for which app.
Does ingress2gateway change anything in the cluster?
No. ingress2gateway print only reads Ingresses (from the cluster or a file) and prints YAML to stdout, with warnings on stderr. You review the output, edit it if needed, and apply it yourself. That makes it safe to run against production to see what a migration would look like.
What happens to annotations it cannot translate?
It prints a warning naming the annotation and the Ingress, and leaves that behaviour out. Snippets, many auth annotations and some controller settings have no Gateway API field; they need an implementation-specific policy object, a change in the app, or a decision to drop them.
Why run the old and new edge side by side?
Because the new Gateway gets its own address, you can test every host against it with curl --resolve or a Host header while real users still use the old one. Nothing changes for users until DNS points to the new address, and pointing it back is the rollback.
How is a canary Ingress translated?
A canary Ingress with a weight becomes weighted backendRefs in one HTTPRoute rule, and a canary by header becomes a separate rule that matches the header. The result is clearer than the annotation pair, because both paths are visible in one route.
Do I have to migrate everything at once?
No, and you should not. Move one host (or one team's hosts) at a time: create its routes, test, change its DNS record, watch errors, then the next. Some Ingresses may stay on another controller that still supports Ingress if they rely on features you cannot replace yet.
In an interview Mid
How would you migrate a cluster from Ingress to Gateway API without downtime?
I would inventory the Ingresses and their annotations, then run ingress2gateway print to get Gateway and HTTPRoute YAML and read the warnings on stderr for what does not translate. After review I apply the Gateway next to the existing controller; it gets its own address, so I can test each host against it with a Host header or curl --resolve while users still hit the old edge. Then I move one host at a time by changing its DNS record, with a low TTL set in advance, watch error rates, and keep the old Ingress until the new path is proven. Rolling back is pointing DNS back.
Also asked: Which Ingress features have no direct Gateway API equivalent? · How do you test a new ingress path before users reach it? · What does ingress2gateway produce, and what does it not do?