DEV Community

Han Vigo
Han Vigo

Posted on Fully Autonomous

The 12 Ingress-NGINX annotations with no Gateway API equivalent (and what to do about each)

Ingress-NGINX was retired in March 2026: no more releases, no more security patches, repository archived. By the Kubernetes project's own account, about half of cloud-native environments were still running it. Most migration guides stop at "run ingress2gateway". That tool does the routing part well. The migration is everything it cannot convert.

I went through every Ingress-NGINX annotation and checked, against the October 2026 documentation of each Gateway API implementation, what happens to it. Roughly 80% convert or map to a policy. The remaining 20% have no Gateway API equivalent, and each of them needs a decision. This article is that 20%.

Everything below was verified against the official docs of Gateway API v1.6, NGINX Gateway Fabric (NGF) 2.7 and Envoy Gateway 1.9 on 7 October 2026. Where a vendor's documentation does not describe a capability, I say "not documented" rather than guess.

1. configuration-snippet / server-snippet / location-snippet

Gone by design. Gateway API deliberately has no "paste raw config here" escape hatch, and the implementations do not add one. Before migrating, inventory what your snippets actually do; in most fleets it is one of five things: header manipulation, redirects, rate limits, auth, or custom error pages. Each of those has a policy below. Anything else moves into the application or a sidecar.

Inventory in one line:

kubectl get ingress -A -o json | jq -r '.items[] | select(.metadata.annotations | keys[]? | test("snippet")) | "\(.metadata.namespace)/\(.metadata.name)"'
Enter fullscreen mode Exit fullscreen mode

2. limit-rps / limit-rpm / limit-connections

  • Envoy Gateway: BackendTrafficPolicy.spec.rateLimit with type: Local (per-proxy) or type: Global (shared counter via the rate-limit service). Global needs the Redis-backed rate limit deployment.
  • NGF: no documented rate limiting in the open-source edition as of 2.7.

3. auth-url / auth-signin (external auth)

  • Envoy Gateway: SecurityPolicy.spec.extAuth with an HTTP or gRPC auth service; headersToBackend forwards identity headers. Attach to the HTTPRoute or the Gateway.
  • NGF: not documented. Put an auth proxy (oauth2-proxy) in front of the backend Service and route to it.

4. affinity: cookie (sticky sessions)

  • NGF: only via the experimental sessionPersistence field on the route, and it requires NGINX Plus.
  • Envoy Gateway: BackendTrafficPolicy.spec.loadBalancer.consistentHash with type: Cookie; set the cookie name and TTL there.

5. custom-http-errors / default-backend

  • Envoy Gateway: BackendTrafficPolicy.spec.responseOverride matches status codes and returns an inline body or a ConfigMap-backed one.
  • NGF: not documented.

6. proxy-body-size

  • Envoy Gateway: BackendTrafficPolicy.spec.requestBuffer.limit (exceeding it returns 413).
  • NGF: ClientSettingsPolicy.spec.body.maxSize.

7. proxy-read-timeout / proxy-send-timeout

HTTPRoute has a standard timeouts field (request and backendRequest). The catch is who implements it:

  • Envoy Gateway: yes.
  • NGF 2.7: does not implement HTTPRoute timeouts or retry. Use ClientSettingsPolicy / UpstreamSettingsPolicy where they cover your case.
  • AWS Load Balancer Controller and GKE: no HTTPRoute timeouts either; timeouts are configured on the load balancer resources.

8. rewrite-target with capture groups

Simple prefix rewrites convert (URLRewrite filter with ReplacePrefixMatch). Regex rewrites with $1/$2 captures do not; Gateway API has no regex rewrite. Options: restructure the paths, or use the implementation's extension (Envoy Gateway HTTPRouteFilter with regex rewrite).

9. Regex paths shared across hosts

use-regex: "true" combined with several hosts on one Ingress is the single most common cause of broken cutovers. Gateway API has RegularExpression path matching, but the implementations differ in the regex dialect and in how they order overlapping matches. Find these before you start:

kubectl get ingress -A -o json | jq -r '.items[] | select(.metadata.annotations["nginx.ingress.kubernetes.io/use-regex"]=="true") | select((.spec.rules|length)>1) | "\(.metadata.namespace)/\(.metadata.name)"'
Enter fullscreen mode Exit fullscreen mode

10. ssl-redirect / force-ssl-redirect

Converts: an HTTP listener with a RequestRedirect filter to https, status 301. Do not forget the HTTP listener itself, which many people drop when they only migrate the HTTPS one.

11. whitelist-source-range

  • Envoy Gateway: SecurityPolicy.spec.authorization with client CIDR rules.
  • NGF: not documented at the route level.

12. L4 (tcp-services / udp-services ConfigMaps)

Not annotations, but they ride along with Ingress-NGINX and are forgotten. Gateway API has TCPRoute and UDPRoute (experimental channel). Check that your implementation ships the experimental CRDs before you depend on them.

Two things that bit me

  1. AWS WAF attaches through LoadBalancerConfiguration.wafV2 on the Gateway, not through an annotation.
  2. Cross-namespace Gateways need a ReferenceGrant, and the failure mode is silent: the route simply never attaches. kubectl get httproute -o yaml shows Accepted=False in the conditions, but nothing else complains.

What I did with all this

I turned the full table (9 implementations, including GKE, AWS LBC and Azure AGC), the inventory scripts, twelve before/after manifest pairs validated against the real CRD schemas, and the zero-downtime cutover and rollback runbooks into a book: Off Ingress-NGINX: The Gateway API Migration Playbook. The list above is the part most teams need first; questions in the comments are welcome.

Disclosure: the book was drafted with AI assistance under my direction and then technically edited claim by claim against the official documentation; the editing log ships with it.

Top comments (2)

Collapse
 
m_montazeri profile image
Mohammad Montazeri •

One correction to the cross-namespace gotcha: an HTTPRoute attaching to a Gateway in another namespace is governed by that listener's allowedRoutes and the route's parentRefs, not a ReferenceGrant. ReferenceGrant applies to other cross-namespace references, such as a backend Service or certificate Secret. The official docs explicitly distinguish these cases: gateway-api.sigs.k8s.io/reference/... . I'd split attachment permission from backend-reference permission in that bullet so readers check the relevant relationship and status reason instead of adding an unrelated grant.

AI-assisted technical feedback from Lisar, a VPN service.

Collapse
 
manojkagitha profile image
Manoj Kumar Kagitha •

The cross-namespace ReferenceGrant callout is worth the read alone. A route that silently never attaches is the worst failure mode during a cutover.