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.
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.
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
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:
ingressClassName→parentRefsspec.rules[].http.paths→spec.rules[].matches[].pathbackend.service.name/port.number→backendRefs[].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→ HTTPRoutespec.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 Gatewaylisteners[].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.
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
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.
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.
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
kubectl --kubeconfig=$KUBE_CONFIG describe httproute <route-name> -n <namespace>
| Reason | Cause | Solution |
|---|---|---|
InvalidListener | Gateway listener is invalid (for example, missing TLS secret) | Check listener Conditions of Gateway and verify TLS secret is created correctly |
NotAllowedByListeners | Hostname of HTTPRoute does not match hostname of Gateway listener | Compare hostnames of HTTPRoute with hostname of Gateway listener and make them match |
NoMatchingParent | Gateway specified in parentRefs cannot be found | Verify name and namespace of Gateway are correct |
RefNotPermitted | No permission to reference Gateway in different namespace | Verify allowedRoutes.namespaces.from of Gateway is set to All, or verify ReferenceGrant is created |
Check case where gateway programmed status is false
kubectl --kubeconfig=$KUBE_CONFIG describe gateway <gateway-name> -n <namespace>
| Reason | Cause | Solution |
|---|---|---|
AddressNotAssigned | Load balancer provisioning failed | Check EXTERNAL-IP status with kubectl get svc -n <gateway-namespace>. If <pending> status continues, there may be issue with load balancer provisioning |
InvalidCertificateRef | TLS secret does not exist or format is invalid | Check whether secret exists with kubectl get secret <name> -n <namespace> and verify type is kubernetes.io/tls |
InvalidGatewayClass | GatewayClass does not exist or is not in Accepted status | Check status with kubectl get gatewayclass |
Check case where traffic is not forwarded to backend service
-
Verify service name and port specified in
backendRefsof HTTPRoute are correct.Check backend servicekubectl --kubeconfig=$KUBE_CONFIG get svc -n <namespace> -
Verify HTTPRoute and backend service are in same namespace. ReferenceGrant is required to reference service in different namespace.
-
Check
ResolvedRefsstatus in Conditions of HTTPRoute.Check ResolvedRefs statuskubectl --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
--providersflag (for example,ingress-nginx,istio,kong) - Verify
gatewayClassNameandnamespaceof automatically generated Gateway resource match actual environment, and modify them if necessary