Help Center/ Cloud Container Engine/ User Guide/ Networking/ Gateway API/ Migrating NGINX Ingress to Envoy Gateway
Updated on 2026-06-05 GMT+08:00

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

  1. Log in to the target node and ensure the node can use kubectl to obtain Ingress resources in the cluster.

    kubectl get ingress

  2. 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

  3. Install ingress2gateway on the node. For details, see Installation.
  4. 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.yaml

    Example 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.

  5. 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

  6. Access the Envoy Gateway.

    1. Obtain the load balancer address of the Envoy Gateway.
      kubectl get gateway nginx -o jsonpath='{.status.addresses}'
    2. 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.com

      Information 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

  7. 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.

    1. 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
    2. 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.

Example: Using ingress2eg for Migration

  1. Log in to the target node and ensure the node can use kubectl to obtain Ingress resources in the cluster.

    kubectl get ingress

  2. 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

  3. Install ingress2eg on the node. For details, see Installation.
  4. 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: ""

  5. 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

  6. Save the output and apply all resources to the cluster.

    ingress2eg print --namespace default > gateway-resources.yaml
    kubectl apply -f gateway-resources.yaml

  7. Access the Envoy Gateway.

    1. Obtain the load balancer address of the Envoy Gateway.
      kubectl get gateway nginx -o jsonpath='{.status.addresses}'
    2. 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.com

      Information 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

  8. 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.

    1. 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
    2. 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.