Migrating NGINX Ingress to Envoy Gateway
Many enterprises use NGINX Ingress as the ingress gateway for Kubernetes clusters to manage external traffic. However, the open-source NGINX Ingress project ceased maintenance and updates in March 2026. Gateway API is recommended as an alternative. You are advised to migrate from NGINX Ingress to Envoy Gateway. This process involves converting existing Ingress resources into Gateway API resources compatible with Envoy Gateway. This section describes how to use a migration tool to migrate from NGINX Ingress to Envoy Gateway, simplifying the migration process.
The Envoy Gateway community provides two tools to assist with the migration (Migrating from Ingress Resources):
- ingress2gateway (basic conversion): an official conversion tool that supports only conversion to Gateway and HTTPRoute resources.
- ingress2eg (enhanced version): an unofficial proof-of-concept tool forked from ingress2gateway. In addition to generating Gateway API resources (Gateway and HTTPRoute), ingress2eg can generate Envoy Gateway-specific CRDs (such as BackendTrafficPolicy and SecurityPolicy).
The migration tools support only some main functions. Pay attention to the warnings generated by the tools. After the Gateway API YAML is generated, manually review the file and verify it in a test environment before migrating traffic.
Prerequisites
- NGINX Ingress Controller is deployed and running in the cluster.
- Envoy Gateway is deployed and running in the cluster.
- kubectl is installed on the node where the migration will be performed, and is connected to the cluster.
Precautions
- In this section, migration refers to creating Gateway and HTTPRoute resources with equivalent configurations. For details about the differences between Gateway API and NGINX Ingress, see Function Comparison Between Envoy Gateway and NGINX Ingress.
- Do not modify the NGINX Ingress configuration during the migration.
- After the migration is complete, do not modify or delete the NGINX Ingress configurations that are serving live traffic. Modify the configurations only after verifying that the new Envoy Gateway functions properly.
- The migrated Envoy Gateway will automatically create a load balancer based on the configuration used during add-on installation.
- If the secret configured for NGINX Ingress is the default NGINX secret, the converted Gateway secret name will be empty and must be manually specified.
Supported Functions
- Functions supported by ingress2gateway
Function
NGINX Ingress Annotation
Gateway API Resource
Description
Grayscale release
canary
canary-weight
canary-weight-total
canary-by-header
canary-by-header-value
HTTPRoute (weight/header matching rules)
Weight- and header-based traffic splitting
Temporary redirection
temporal-redirect
temporal-redirect-code
HTTPRoute (RequestRedirect filter, 302)
Temporary URL redirection. The status code (301/302) can be customized.
Permanent redirection
permanent-redirect
permanent-redirect-code
HTTPRoute (RequestRedirect filter, 301)
Permanent URL redirection. The status code (301/302) can be customized.
Redirection to www
from-to-www-redirect
HTTPRoute (RequestRedirect filter, 301)
Redirect from www to non-www domain names
Request header modification
upstream-vhost
connection-proxy-header
HTTPRoute (RequestHeaderModifier filter)
Perform operations on request headers (including Host and Connection).
Timeout
proxy-connect-timeout
proxy-send-timeout
proxy-read-timeout
HTTPRoute (Timeouts.Request)
TCP timeout converted to HTTP request timeout
CORS
enable-cors
cors-allow-origin
cors-allow-methods
cors-allow-headers
cors-allow-credentials
cors-expose-headers
cors-max-age
HTTPRoute (CORS configuration)
Cross-origin resource sharing configuration
- Functions supported by ingress2eg
For details, see the official description.
Example: Using ingress2gateway for Migration
- Log in to the target node and ensure the node can use kubectl to obtain Ingress resources in the cluster.
kubectl get ingress
- Check the IngressClass name of the NGINX Ingress Controller installed in the cluster.
kubectl get ingressclass
Information similar to the following is displayed:
NAME CONTROLLER PARAMETERS AGE nginx k8s.io/ingress-nginx <none> 29h nginx2 k8s.io/ingress-nginx-nginx2 <none> 27s
- Install ingress2gateway on the node. For details, see Installation.
- Use ingress2gateway to convert NGINX Ingress resources to Gateway API YAML and save the output. In the command below, replace {IngressClass-name} with the value obtained in step 2. Select an Ingress resource under one of the controllers for conversion.
./ingress2gateway print --providers=ingress-nginx --ingress-nginx-ingress-class={IngressClass-name} > nginx.yamlExample nginx.yaml configuration:
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-1.1.0 name: nginx namespace: default spec: gatewayClassName: nginx # Change it to the name of an existing GatewayClass in the cluster, for example, envoy-gateway. listeners: - hostname: www.example.com name: www-example-com-http port: 80 protocol: HTTP - hostname: www.example.com name: www-example-com-https port: 443 protocol: HTTPS tls: certificateRefs: - group: "" kind: Secret name: "" # Change it to the secret name. --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-1.1.0 name: test2-www-example-com namespace: default spec: hostnames: - www.example.com parentRefs: - name: nginx port: 443 sectionName: www-example-com-https rules: - backendRefs: - name: nginx port: 8080 matches: - path: type: PathPrefix value: /a - backendRefs: - name: nginx-2 port: 9001 matches: - path: type: PathPrefix value: / status: parents: [] --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2gateway-1.1.0 name: test2-www-example-com-http namespace: default spec: hostnames: - www.example.com parentRefs: - name: nginx port: 80 rules: - filters: - requestRedirect: scheme: https statusCode: 308 # Gateway API supports only 301 and 302. Manually change the status code. type: RequestRedirect matches: - path: type: PathPrefix value: /a - filters: - requestRedirect: scheme: https statusCode: 308 # Gateway API supports only 301 and 302. Manually change the status code. type: RequestRedirect matches: - path: type: PathPrefix value: / status: parents: null
- httproute.spec.rules.filters.requestRedirect.statusCode supports only 301 and 302. Manually modify the configuration before proceeding.
- If the secret configured for NGINX Ingress is the default NGINX secret, the converted Gateway secret name will be empty and must be manually specified.
- Create a Gateway and an HTTPRoute.
kubectl apply -f nginx.yaml
Information similar to the following is displayed:
gateway.gateway.networking.k8s.io/nginx created httproute.gateway.networking.k8s.io/test2-all-hosts created
- Access the Envoy Gateway.
- Obtain the load balancer address of the Envoy Gateway.
kubectl get gateway nginx -o jsonpath='{.status.addresses}' - Access the private IP address of the load balancer from a node in the same VPC.
curl -ik --resolve www.example.com:443:xx.xx.xx.xx https://www.example.comInformation similar to the following is displayed:
HTTP/2 200 OK server: nginx/1.19.3 date: Sat, 16 May 2026 09:04:17 GMT content-type: text/html content-length: 612 last-modified: Tue, 29 Sep 2020 14:12:31 GMT etag: "5f7340cf-264" accept-ranges: bytes
- Obtain the load balancer address of the Envoy Gateway.
- Migrate traffic.
Do not modify or delete the NGINX Ingress configurations that are serving live traffic. Modify the configurations only after verifying that the new Envoy Gateway functions properly.
- Point the domain name to the new IP address of the Gateway and switch 1% to 5% of traffic to the new Gateway. Observe service status, including the following metrics:
- Request success rate
- API latency (P99/P95)
- Error logs (4xx/5xx)
- ELB backend health status
- Gradually increase traffic.
- Increase the traffic percentage to 10%, 30%, and then 50%.
- Continue observing the metrics. If no anomalies occur, gradually increase traffic until all traffic is switched to the new Gateway.
- Point the domain name to the new IP address of the Gateway and switch 1% to 5% of traffic to the new Gateway. Observe service status, including the following metrics:
Example: Using ingress2eg for Migration
- Log in to the target node and ensure the node can use kubectl to obtain Ingress resources in the cluster.
kubectl get ingress
- Check the IngressClass name of the NGINX Ingress Controller installed in the cluster.
kubectl get ingressclass
Information similar to the following is displayed:
NAME CONTROLLER PARAMETERS AGE nginx k8s.io/ingress-nginx <none> 29h nginx2 k8s.io/ingress-nginx-nginx2 <none> 27s
- Install ingress2eg on the node. For details, see Installation.
- Check the Ingress resources to be migrated. Example:
apiVersion: v1 items: - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: creationTimestamp: "2026-05-14T06:45:11Z" generation: 3 name: test2 namespace: default resourceVersion: "2749120" uid: 36f54e81-e9ca-4f8b-a303-723ad36022bc spec: ingressClassName: nginx rules: - host: www.example.com http: paths: - backend: service: name: nginx port: number: 8080 path: /a pathType: Prefix property: ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH tls: - {} status: loadBalancer: ingress: - ip: 172.16.1.26 - apiVersion: networking.k8s.io/v1 kind: Ingress metadata: annotations: nginx.ingress.kubernetes.io/limit-rps: "10" creationTimestamp: "2026-05-13T07:58:56Z" generation: 6 name: www namespace: default resourceVersion: "2770503" uid: 9db1744f-ff72-4b43-9c6d-efefeb6d4310 spec: ingressClassName: nginx rules: - host: www.example.com http: paths: - backend: service: name: nginx-2 port: number: 9001 path: / pathType: ImplementationSpecific property: ingress.beta.kubernetes.io/url-match-mode: STARTS_WITH status: loadBalancer: ingress: - ip: 172.16.1.26 kind: List metadata: resourceVersion: "" - Run ingress2eg to convert the Ingress resources.
# Convert from cluster resources in a specific namespace. ./ingress2eg print --namespace default --ingress-nginx-ingress-class={IngressClass-name}Expected output:
apiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: annotations: gateway.networking.k8s.io/generator: ingress2eg-dev name: nginx namespace: default spec: gatewayClassName: eg # Change it to the name of an existing GatewayClass in the cluster, for example, envoy-gateway. listeners: - hostname: www.example.com name: www-example-com-http port: 80 protocol: HTTP - hostname: www.example.com name: www-example-com-https port: 443 protocol: HTTPS tls: certificateRefs: - group: "" kind: Secret name: "" status: {} --- apiVersion: gateway.networking.k8s.io/v1 kind: HTTPRoute metadata: annotations: gateway.networking.k8s.io/generator: ingress2eg-dev name: test2-www-example-com # Name namespace: default spec: hostnames: - www.example.com parentRefs: - name: nginx rules: - backendRefs: - name: nginx port: 8080 matches: - path: type: PathPrefix value: /a name: rule-0-prefix-a - backendRefs: - name: nginx-2 port: 9001 matches: - path: type: PathPrefix value: / name: rule-1-impl-specific status: parents: [] --- apiVersion: gateway.envoyproxy.io/v1alpha1 kind: BackendTrafficPolicy metadata: annotations: gateway.networking.k8s.io/generator: ingress2eg-dev name: test2-www-example-com-1 namespace: default spec: rateLimit: local: rules: - clientSelectors: - sourceCIDR: type: Distinct value: 0.0.0.0/0 limit: requests: 10 unit: Second targetRefs: - group: gateway.networking.k8s.io kind: HTTPRoute name: test2-www-example-com sectionName: rule-1-impl-specific status: ancestors: null - Save the output and apply all resources to the cluster.
ingress2eg print --namespace default > gateway-resources.yaml kubectl apply -f gateway-resources.yaml
- Access the Envoy Gateway.
- Obtain the load balancer address of the Envoy Gateway.
kubectl get gateway nginx -o jsonpath='{.status.addresses}' - Access the private IP address of the load balancer from a node in the same VPC.
curl -ik --resolve www.example.com:443:xx.xx.xx.xx https://www.example.comInformation similar to the following is displayed:
HTTP/2 200 OK server: nginx/1.19.3 date: Sat, 16 May 2026 09:04:17 GMT content-type: text/html content-length: 612 last-modified: Tue, 29 Sep 2020 14:12:31 GMT etag: "5f7340cf-264" accept-ranges: bytes
- Obtain the load balancer address of the Envoy Gateway.
- Migrate traffic.
Do not modify or delete the NGINX Ingress configurations that are serving live traffic. Modify the configurations only after verifying that the new Envoy Gateway functions properly.
- Point the domain name to the new IP address of the Gateway and switch 1% to 5% of traffic to the new Gateway. Observe service status, including the following metrics:
- Request success rate
- API latency (P99/P95)
- Error logs (4xx/5xx)
- ELB backend health status
- Gradually increase traffic.
- Increase the traffic percentage to 10%, 30%, and then 50%.
- Continue observing the metrics. If no anomalies occur, gradually increase traffic until all traffic is switched to the new Gateway.
- Point the domain name to the new IP address of the Gateway and switch 1% to 5% of traffic to the new Gateway. Observe service status, including the following metrics:
Feedback
Was this page helpful?
Provide feedbackThank you very much for your feedback. We will continue working to improve the documentation.See the reply and handling status in My Cloud VOC.
For any further questions, feel free to contact us through the chatbot.
Chatbot