Skip to main content

Migrate from Ingress to Gateway API

Provide guidance on how to convert existing Ingress resources to Gateway API.
For conceptual comparison between Ingress and Gateway API, refer to Gateway API deployment > Compare Ingress and Gateway API.

oaicite:0

info

This guide provides method to transition to Gateway API for safe infrastructure operation following retirement of community NGINX Ingress Controller (kubernetes/ingress-nginx) (maintenance ends March 2026).

  • Background for recommended migration: Existing environments do not stop immediately, but security patches and bug fixes will no longer be available.
  • Select Gateway API implementation: This guide uses NGINX Gateway Fabric as example, but Envoy, Istio, Cilium, and others can be freely selected.
  • Official references: Kubernetes official statement and Gateway API implementation list.
caution

Require pre-validation: This document is reference guide to assist migration. Kubernetes Engine does not take responsibility for service disruption or traffic loss during transition. Perform sufficient validation in staging environment before applying to production.

Annotation compatibility: Custom annotations in existing Ingress resources are not mapped 1:1 to Gateway API (for example, rewrite-target, proxy-body-size, ssl-redirect). Conversion method varies depending on selected Gateway API controller. Check current configuration and apply individually based on official documentation. Refer to Kubernetes Gateway API migration guide and Ingress-NGINX migration guide.

Configure admission webhook: Communication for Admission Webhook is restricted by default in Kubernetes Engine. If selected Gateway API controller includes components that use Admission Webhook, 반드시 해당 파드에 hostNetwork: true 설정을 추가해야 합니다.

Configure load balancer IP: Load balancer created through Gateway in Kubernetes Engine is assigned private IP by default. If public IP for external communication is required, configure additional load balancer annotation. Refer to load balancer guide for details.

Step 1. Perform automatic conversion using ingress2gateway

Use ingress2gateway tool provided by Kubernetes SIG-Network to automatically convert existing Ingress resources into Gateway API resources (Gateway + HTTPRoute).

Install ingress2gateway

go install github.com/kubernetes-sigs/ingress2gateway@latest

Convert from cluster

Read Ingress resources deployed in current cluster and convert them into Gateway API resources.

ingress2gateway print --kubeconfig=$KUBE_CONFIG --providers=ingress-nginx --all-namespaces
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: nginx
namespace: demo-apps
spec:
gatewayClassName: nginx
listeners:
- name: http
port: 80
protocol: HTTP
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: path-routing-all-hosts
namespace: demo-apps
spec:
parentRefs:
- name: nginx
rules:
- backendRefs:
- name: app-v1
port: 80
matches:
- path:
type: PathPrefix
value: /api
- backendRefs:
- name: app-v2
port: 80
matches:
- path:
type: PathPrefix
value: /web

Convert from file

Convert Ingress YAML file directly without connecting to cluster.

ingress2gateway print --providers=ingress-nginx --input-file=ingress-backup.yaml
ingress2gateway print --providers=ingress-nginx --input-file=ingress-backup.yaml > gateway-api-resources.yaml
caution

ingress2gateway converts core Ingress fields such as host, path, backend, and TLS, and also converts some commonly used annotations in ingress-nginx.

However, not all annotations and implementation-specific extensions are automatically converted 1:1. 반드시 검토해야 합니다: gatewayClassName, namespace, hostnames, filters, backendRefs.

Step 2. Map fields

When performing manual conversion without using automatic tool, refer to following Before/After examples. Examples assume Gateway resource is already created.

Example 1. Route based on path

Before — Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: path-routing
namespace: demo-apps
spec:
ingressClassName: nginx
rules:
- http:
paths:
- path: /api
pathType: Prefix
backend:
service:
name: app-v1
port:
number: 80
- path: /web
pathType: Prefix
backend:
service:
name: app-v2
port:
number: 80

After — HTTPRoute

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: path-routing
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
rules:
- matches:
- path:
type: PathPrefix
value: /api
backendRefs:
- name: app-v1
port: 80
- matches:
- path:
type: PathPrefix
value: /web
backendRefs:
- name: app-v2
port: 80

Key changes:

  • ingressClassNameparentRefs
  • spec.rules[].http.pathsspec.rules[].matches[].path
  • backend.service.name / port.numberbackendRefs[].name / port

Example 2. Route based on host

Before — Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: host-routing
namespace: demo-apps
spec:
ingressClassName: nginx
rules:
- host: api.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: api-svc
port:
number: 80
- host: web.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web-svc
port:
number: 80

After — HTTPRoute

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: api-route
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
hostnames:
- api.example.com
rules:
- backendRefs:
- name: api-svc
port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: web-route
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
hostnames:
- web.example.com
rules:
- backendRefs:
- name: web-svc
port: 80

Key changes:

  • spec.rules[].host → HTTPRoute spec.hostnames
  • Create separate HTTPRoute per host for independent management (multiple hostnames can also be defined in single HTTPRoute)

Example 3. Configure tls termination

Before — Ingress

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: tls-ingress
namespace: demo-apps
spec:
ingressClassName: nginx
tls:
- hosts:
- secure.example.com
secretName: tls-secret
rules:
- host: secure.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: secure-svc
port:
number: 80

After — Gateway + HTTPRoute

In Gateway API, TLS configuration is defined in listener of Gateway resource. HTTPRoute handles only routing rules.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: nginx-gateway
namespace: nginx-gateway
spec:
gatewayClassName: nginx
listeners:
- name: https
port: 443
protocol: HTTPS
hostname: secure.example.com
tls:
mode: Terminate
certificateRefs:
- name: tls-secret
allowedRoutes:
namespaces:
from: All
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: secure-route
namespace: demo-apps
spec:
parentRefs:
- name: nginx-gateway
namespace: nginx-gateway
sectionName: https
hostnames:
- secure.example.com
rules:
- backendRefs:
- name: secure-svc
port: 80

Key changes:

  • Ingress spec.tls → configure TLS termination in Gateway listeners[].tls
  • TLS certificate (Secret) is managed by Gateway, HTTPRoute defines only routing
  • Reference specific listener using parentRefs.sectionName

Step 3. Complete prerequisites

Complete following prerequisites before starting migration.

1. Backup existing ingress configuration

kubectl --kubeconfig=$KUBE_CONFIG get ingress -A -o yaml > ingress-backup.yaml

2. Install gateway api crd

Refer to Gateway API deployment guide.

kubectl --kubeconfig=$KUBE_CONFIG get crd | grep gateway

3. Deploy gateway api controller

kubectl --kubeconfig=$KUBE_CONFIG get pods -n nginx-gateway

4. Create gateway resource

kubectl --kubeconfig=$KUBE_CONFIG get gateway -n nginx-gateway
NAME            CLASS   ADDRESS                                                                 PROGRAMMED   AGE
nginx-gateway nginx k8s-nginxga-nginxga-xxxxxxxxxx-xxxxxx.ke.kr-central-2.kakaocloud.com True 3m

Step 4. Perform migration

Convert existing Ingress resources to Gateway API following order: parallel run → validate → switch.

1. Create httproute resource

kubectl --kubeconfig=$KUBE_CONFIG apply -f httproute-path.yaml
kubectl --kubeconfig=$KUBE_CONFIG get httproute -n demo-apps

2. Run in parallel

Operate existing Ingress and new HTTPRoute simultaneously and verify traffic is handled correctly in both.

info

Ingress Controller and Gateway API Controller create separate load balancers, so they can operate simultaneously without interference. However, existing load balancer IP used by Ingress cannot be reused in Gateway API. Update DNS record to new load balancer address.

kubectl --kubeconfig=$KUBE_CONFIG get ingress -n demo-apps
kubectl --kubeconfig=$KUBE_CONFIG get gateway -n nginx-gateway -o jsonpath='{.items[0].status.addresses[0].value}'

3. Switch traffic and delete ingress

kubectl --kubeconfig=$KUBE_CONFIG delete ingress path-routing -n demo-apps
caution

Deleting Ingress resource stops traffic through existing Ingress Controller load balancer. Verify Gateway API traffic works correctly before deletion.

4. Roll back plan

If issue occurs after migration, restore Ingress resources.

kubectl --kubeconfig=$KUBE_CONFIG apply -f ingress-backup.yaml
kubectl --kubeconfig=$KUBE_CONFIG delete httproute path-routing -n demo-apps

Restore DNS to original Ingress load balancer address.

info

Keep existing Ingress Controller until Gateway API traffic is fully stabilized to allow rollback.

Step 5. Verify migration

1. Check gateway and httproute status

Verify Gateway has ADDRESS assigned and PROGRAMMED: True, and HTTPRoute has Accepted: True.

kubectl --kubeconfig=$KUBE_CONFIG get gateway -n nginx-gateway
kubectl --kubeconfig=$KUBE_CONFIG describe httproute path-routing -n demo-apps
Status:
Parents:
Conditions:
Message: The route is accepted
Reason: Accepted
Status: True
Type: Accepted
Message: All references are resolved
Reason: ResolvedRefs
Status: True
Type: ResolvedRefs

2. Validate routing behavior

Check routing using Gateway ADDRESS.

export GW_ADDRESS=$(kubectl --kubeconfig=$KUBE_CONFIG get gateway nginx-gateway -n nginx-gateway -o jsonpath='{.status.addresses[0].value}')
echo $GW_ADDRESS
curl -v http://$GW_ADDRESS/api
curl -v http://$GW_ADDRESS/web
curl -v http://$GW_ADDRESS/ -H "Host: api.example.com"
curl -v http://$GW_ADDRESS/ -H "Host: web.example.com"
curl -v https://$GW_ADDRESS/ -H "Host: secure.example.com" --insecure

If each request is routed to correct backend service, migration is successfully completed.

info

After migration, remove unused Ingress Controller resources (Deployment, Service, IngressClass) to prevent unnecessary resource usage.

Troubleshoot

These are common issues that can occur during migration and how to resolve them.

Check case where httproute accepted status is false

Check HTTPRoute status
kubectl --kubeconfig=$KUBE_CONFIG describe httproute <route-name> -n <namespace>
ReasonCauseSolution
InvalidListenerGateway listener is invalid (for example, missing TLS secret)Check listener Conditions of Gateway and verify TLS secret is created correctly
NotAllowedByListenersHostname of HTTPRoute does not match hostname of Gateway listenerCompare hostnames of HTTPRoute with hostname of Gateway listener and make them match
NoMatchingParentGateway specified in parentRefs cannot be foundVerify name and namespace of Gateway are correct
RefNotPermittedNo permission to reference Gateway in different namespaceVerify allowedRoutes.namespaces.from of Gateway is set to All, or verify ReferenceGrant is created

Check case where gateway programmed status is false

Check Gateway status
kubectl --kubeconfig=$KUBE_CONFIG describe gateway <gateway-name> -n <namespace>
ReasonCauseSolution
AddressNotAssignedLoad balancer provisioning failedCheck EXTERNAL-IP status with kubectl get svc -n <gateway-namespace>. If <pending> status continues, there may be issue with load balancer provisioning
InvalidCertificateRefTLS secret does not exist or format is invalidCheck whether secret exists with kubectl get secret <name> -n <namespace> and verify type is kubernetes.io/tls
InvalidGatewayClassGatewayClass does not exist or is not in Accepted statusCheck status with kubectl get gatewayclass

Check case where traffic is not forwarded to backend service

  1. Verify service name and port specified in backendRefs of HTTPRoute are correct.

    Check backend service
    kubectl --kubeconfig=$KUBE_CONFIG get svc -n <namespace>
  2. Verify HTTPRoute and backend service are in same namespace. ReferenceGrant is required to reference service in different namespace.

  3. Check ResolvedRefs status in Conditions of HTTPRoute.

    Check ResolvedRefs status
    kubectl --kubeconfig=$KUBE_CONFIG get httproute <route-name> -n <namespace> -o jsonpath='{.status.parents[0].conditions[?(@.type=="ResolvedRefs")]}'

Check case where ingress2gateway conversion result differs from expectation

  • Verify correct provider is specified in --providers flag (for example, ingress-nginx, istio, kong)
  • Verify gatewayClassName and namespace of automatically generated Gateway resource match actual environment, and modify them if necessary