Migrating from ingress-nginx

How to move from managed ingress-nginx to Envoy Gateway, in direct or proxy mode

This guide helps you move an existing workload from our managed ingress-nginx to Envoy Gateway. The ingress concepts are the same (listeners, routes, TLS, and a choice between direct and proxy mode), but the resources you write are Gateway API objects instead of Ingress objects.

If you are not migrating but setting up fresh, start from the Envoy Gateway overview instead.

What stays the same

  • Ingress enters through one load balancer. A single shared Gateway gets one OpenStack load balancer and IP, just as ingress-nginx did. Your public DNS points at that address.
  • Direct vs proxy is still a cluster-level mode, set by us: the same choice you made with ingress-nginx (use-proxy-protocol vs use-forwarded-headers), just under new names.

Step 0: Confirm your mode (direct or proxy)

The mode must match how your cluster’s load balancer is provisioned. It is a cluster-level setting that we manage; if you are unsure which one your cluster runs, ask support before you cut over.

  • Direct (PROXY-protocol) mode: clients connect straight to our load balancer. The load balancer carries the real client IP with PROXY protocol v2. No CDN or upstream proxy is involved. This is the default and the equivalent of ingress-nginx use-proxy-protocol: "true".
  • Proxy (X-Forwarded-For) mode: you operate your own CDN / WAF / edge proxy in front of the load balancer, and it injects X-Forwarded-For. The equivalent of ingress-nginx use-forwarded-headers: "true".

Proxy mode requires your own upstream proxy. It only makes sense if a CDN, WAF, or edge proxy actually sits in front of the load balancer and injects the header. Without one, no real client IP reaches Envoy and your backends see only the load balancer. If clients connect directly to Elastx, use direct (PROXY-protocol) mode; it needs no CDN.

Once you know your mode, the per-resource walkthrough lives in:

Resource mapping

ingress-nginx Envoy Gateway / Gateway API
IngressClass nginx GatewayClass eg (cluster-managed; you only reference it)
Ingress (one object, implicit listeners) Gateway (explicit listeners, ports, TLS) + HTTPRoute (routing rules)
spec.tls on the Ingress HTTPS listener tls.certificateRefs on the Gateway
use-proxy-protocol / use-forwarded-headers (controller ConfigMap) Cluster mode (Elastx) + your ClientTrafficPolicy
nginx.ingress.kubernetes.io/* annotations HTTPRoute filters, BackendTrafficPolicy, SecurityPolicy
cert-manager Issuer with solvers.http01.ingress.class: nginx cert-manager Issuer with solvers.http01.gatewayHTTPRoute.parentRefs
TCP/UDP services (tcp-services ConfigMap) TCPRoute / UDPRoute (see note below)

Note: Our Envoy Gateway ships the Gateway API standard channel, which provides only HTTPRoute and GRPCRoute. TCPRoute, TLSRoute and UDPRoute are not installed. If you relied on the nginx tcp-services ConfigMap, contact support before migrating those.

Migration steps

  1. Ask support to turn on Envoy Gateway for your cluster, and confirm your mode (direct or proxy; see Step 0). ingress-nginx keeps running alongside it, so you can move one host at a time.
  2. Make sure cert-manager can serve Gateway API. If you use our managed cert-manager, we turn on its Gateway API support together with Envoy Gateway. If you run your own, install it (see Install and upgrade cert-manager) and start it with Gateway API support enabled (--enable-gateway-api), or the HTTP-01 gatewayHTTPRoute solver will not work.
  3. Recreate your ingress as Gateway API objects following the guide for your mode (direct or proxy). The standard layout is a shared Gateway in a dedicated gateway namespace, with a matching ClientTrafficPolicy and a TLS Certificate per hostname, that your application namespaces attach to. Each app namespace then only carries the shared-gateway-access: "true" label and one HTTPRoute per host. A single ingress-nginx controller served all your hosts through one entry point before; one shared Gateway is its direct equivalent, keeping your cluster on a single load balancer.
  4. Translate annotations. Path rewrites, header manipulation and redirects become HTTPRoute filters; retries and circuit breaking become a BackendTrafficPolicy; auth/CORS become a SecurityPolicy.
  5. Switch your cert-manager Issuer to a Gateway-aware solver that issues a certificate per hostname: the http01.gatewayHTTPRoute solver in direct mode, or a DNS-01 Issuer in proxy mode (where public DNS points at your upstream proxy). Each mode guide shows the one for that mode.
  6. Test before cutover without touching DNS, by resolving your hostname to the new Gateway address locally:
    GW=$(kubectl -n gateway get gateway shared -o jsonpath='{.status.addresses[0].value}')
    curl -v --resolve your.host.example.com:443:"$GW" https://your.host.example.com/
    
  7. Cut over DNS to the Gateway’s load-balancer address. Use a low TTL (≈1 minute) beforehand so the change propagates quickly, and a CNAME where possible so only one record needs updating.
  8. Clean up once traffic is confirmed on Envoy Gateway: delete your old Ingress objects, and ask support to remove our managed ingress-nginx from your cluster when nothing uses it any more.

Advanced usage

For more advanced use cases please refer to the documentation provided by each project or contact our support:

Last modified November 2, 2026: Add Envoy Gateway customer guides (#297) (fc663a2)