商店現已上線

我們打造用於網站、AI 智慧體和 LLM 訓練的 Workstation AI 機器——線上配置併購買。

獲取同行價格
AI 產品
AI 實驗室OpenAI 智慧體Claude 代理Grok BotWorkstation AI 原生 CRM營銷所有產品
AI 解決方案
應用型 AIAI 智慧體生成式 AIAI 自動化AI 技能提升中小企業 AI 套餐企業 AI 實驗室AI 工作站NVIDIA 硬體與軟體NVIDIA 加速計算私有 AIGPU 私有云GPU 叢集邊緣 AI行業 AI
AI 服務
平臺現代化數字工程資料基礎與 AI自主運營AI 諮詢DevOps 自動化網路安全軟體開發智慧體構建MLOps 搭建GPU 租賃GPU 私有云
關於我們
公司我們的故事合作伙伴客戶案例
AI 資料中心
AI 資料中心GPU 私有云GPU 叢集GPU 租賃
AI 洞察Jev 系列基準測試實驗室模型選擇
技術說明
文件
Linux 作業系統UbuntuRed Hat Enterprise LinuxSUSE LinuxAlpine LinuxDebian、Rocky 與 AlmaLinux
AI 雲平臺AWS 與 Amazon BedrockGoogle Cloud 與 Vertex AIMicrosoft Azure 與 AI FoundryGPU 雲與算力市場
私有 AI 資料中心面向律師事務所面向會計師事務所本地部署平臺全面安全的雲
工具Ring PromoterJobshout
新聞
部落格
聯絡我們登入

面向現代企業的 AI 工作站、AI 多智慧體軟體、GPU 基礎設施和智慧代理解決方案。

聯絡我們

AI 解決方案

應用型 AIAI 智慧體生成式 AIAI 自動化AI 技能提升中小企業 AI 套餐企業 AI 實驗室行業 AI

產品

所有產品Workstation AI native CRM 與 ERP營銷OpenAI 智慧體Ring PromoterJobshout

公司

關於我們公司AI 資料中心為什麼選擇Workstation合作伙伴客戶案例定價聯絡TenthMatrix

資源

AI 洞察技術說明文件Linux 作業系統AI 雲平臺私有 AI 資料中心部落格搜尋網站地圖
英國總部
77-79 Marlowes, Hemel Hempstead HP1 1LF路線指引 — 從 M25 外環倫敦 20 號出口駛出公司編號: 11641870週一至週五:上午 9:00 - 下午 6:00 GMT
+44 7515 356 146
比利時辦公室
Workstation SRL, Rue Vanderkindere 34, 1180 Uccle, BrusselsBE 0751.518.683週一至週五:上午 9:00 - 下午 6:00 CET
+32 492 45 67 46
印度辦公室
#159 Sector 9, Pocket 1, DDA Flats, 110077 Dwarka, New Delhi
+91 98881 98841
台灣辦公室
3F, No. 2-4, Aisi Rd., Ren'ai Dist., Keelung City 200, Taiwan基隆市仁愛區愛四路2之4號3樓週一至週五:上午 9:00 - 下午 6:00 臺灣時間(GMT+8)
+886 926 892 701+32 492 45 67 46

© 2026 Workstation AI。保留所有權利。

隱私資料刪除Cookie服務條款網站地圖

NVIDIA、NVIDIA 標誌、RTX、RTX PRO、Blackwell、CUDA、NVLink、MGX、Omniverse、Spectrum-X、Quantum、BlueField、DGX 與 HGX 為 NVIDIA Corporation 在美國及其他國家的商標及/或註冊商標。AMD、AMD Arrow 標誌、EPYC、Instinct、Ryzen 與 Threadripper 為 Advanced Micro Devices, Inc. 的商標。Dell 與 PowerEdge 為 Dell Inc. 或其子公司的商標。Intel 與 Xeon 為 Intel Corporation 或其子公司的商標。AMAX、AceleMax 與 LiquidMax 為 AMAX Engineering Corporation 的商標。所有其他產品名稱、標誌與品牌均為其各自所有者之財產。我們使用它們僅為識別我們所描述、供應或支援的產品;使用這些名稱並不表示其所有者為我們背書。

Loading blog...

首頁 / 部落格
DevOpsKubernetesNetworkingPlatform EngineeringSRE

Gateway API Replaces Ingress: A Step-by-Step Migration Playbook

JobShout AI Blog Writer Agent (opens in a new window)2026年11月4日10 min read
Cover

Ingress NGINX is retired and the Ingress API is frozen, so most Kubernetes teams now have a migration to plan. The YAML conversion is the easy part. Outages come from behaviour the old controller applied silently, and from cutovers with no tested way back. This playbook covers both, one service at a time.

In one sentence. Convert each Ingress to a Gateway and HTTPRoute, run them side by side with the old controller, prove they return the same answers, then move traffic in small reversible steps and keep the old path until the new one has carried real load.

This is the hands-on companion to Gateway API shifts cluster networking, which covers the platform design decisions (who owns the Gateway API CRDs, gateway topology, RBAC). Settle those first; this post assumes they are made.

Why migrate now

The Kubernetes project announced that Ingress NGINX maintenance would stop in March 2026, with "no further releases, no bugfixes, and no updates to resolve any security vulnerabilities" after that. Existing deployments keep working, and the project recommended that users "begin migration to Gateway API or another Ingress controller immediately" [1]. Separately, the Ingress API itself is frozen: it remains GA and will not be removed, but it will receive no further changes [2].

Staying on Ingress with a different, maintained controller is a legitimate choice. If you are moving controllers anyway, though, moving to Gateway API at the same time avoids doing the migration twice.

How Ingress maps to Gateway API

The official migration guide lists the structural differences [3]. Entry points that the Ingress controller created implicitly must now be declared as Gateway listeners. TLS termination moves from the Ingress to the listener. Routes choose their Gateway with parentRefs, rather than choosing a controller by class. There is no default backend: you write an explicit catch-all rule.

Where each Ingress field goes in Gateway API. On the left, the fields of an Ingress: ingressClassName, tls.secretName, rules host, paths with pathType, backend service, default backend, and annotations. Arrows lead to the right. ingressClassName and tls.secretName go to the Gateway as gatewayClassName and listener certificateRefs. Host, paths and backend service go to the HTTPRoute as hostnames, matches and backendRefs. The default backend becomes a PathPrefix slash rule in the HTTPRoute. Annotations split into standard HTTPRoute filters or implementation-specific policies.
One Ingress becomes at least two objects with different owners. Annotations are the part that needs human review: some become standard filters, some need an implementation-specific policy, and some have no equivalent.

A worked example

Here is a typical Ingress for an ingress-nginx controller: one host, TLS, an API path and a catch-all.

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: shop
  namespace: shop
  annotations:
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts: ["shop.example.com"]
    secretName: shop-example-com
  rules:
  - host: shop.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api
            port:
              number: 8080
      - path: /
        pathType: Prefix
        backend:
          service:
            name: web
            port:
              number: 80

The Gateway API equivalent is three objects. The HTTP-to-HTTPS redirect, which was an annotation, becomes an explicit route on the port 80 listener using the standard RequestRedirect filter [3]. A redirect rule cannot also have backendRefs; the API's validation rules reject that combination [11].

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: shop-gw
  namespace: shop
spec:
  gatewayClassName: example-class   # from your chosen implementation
  listeners:
  - name: http
    port: 80
    protocol: HTTP
    hostname: shop.example.com
  - name: https
    port: 443
    protocol: HTTPS
    hostname: shop.example.com
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: shop-example-com
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: shop
spec:
  parentRefs:
  - name: shop-gw
    sectionName: https
  hostnames: ["shop.example.com"]
  rules:
  - matches:
    - path: { type: PathPrefix, value: /api }
    backendRefs:
    - name: api
      port: 8080
  - matches:
    - path: { type: PathPrefix, value: / }   # replaces the catch-all
    backendRefs:
    - name: web
      port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop-https-redirect
  namespace: shop
spec:
  parentRefs:
  - name: shop-gw
    sectionName: http
  hostnames: ["shop.example.com"]
  rules:
  - filters:
    - type: RequestRedirect
      requestRedirect:
        scheme: https
        statusCode: 301

If your platform team runs a shared Gateway in another namespace, the HTTPRoute's parentRefs gains a namespace, and the Gateway must admit your namespace through allowedRoutes. The companion post explains that handshake.

Annotations: what maps, and what does not

ingress-nginx annotationGateway API approachNotes
ssl-redirectRequestRedirect filter on the HTTP listenerStandard [3]
rewrite-targetURLRewrite filterDoes not turn matches into regex, unlike ingress-nginx [4]
canary-weightWeighted backendRefsWeights are proportions, not percentages [8]
canary-by-headerA rule with a headers matchHeader matching is part of the core API
enable-corsHTTPRoute CORS filterStandard since v1.5 [10]
proxy-read-timeouttimeouts.requestBest-effort mapping only; semantics differ [5]
proxy-body-sizeNo standard fieldNeeds an implementation-specific policy [5]
configuration-snippetNo equivalentRewrite the intent with your implementation's features [5]

Five behaviours that will bite

The Kubernetes blog published a list of ingress-nginx behaviours that do not carry over, because conformant Gateway API implementations do not reproduce them [4]. Each can turn a working URL into a 404.

  1. Regex matches are prefix-based and case-insensitive in ingress-nginx. Several Gateway API implementations do full, case-sensitive matches, so add .* or (?i) deliberately if you relied on that.
  2. use-regex applies to every path for that host, across all Ingresses. A typo that ingress-nginx tolerated as a regex becomes a 404 under an Exact match.
  3. rewrite-target silently enables regex. URLRewrite does not.
  4. Missing trailing slashes are redirected with a 301 in some cases. The post states that "conformant Gateway API implementations do not silently configure any kind of redirects", so add an explicit redirect rule if clients depend on it.
  5. URLs are normalised before matching. Normalisation varies by implementation; check that backends do not rely on it.

The migration playbook

Run this per service, or per hostname, rather than per cluster. Small batches make every step reversible.

  1. Inventory. List controllers and Ingresses, and export them to files. kubectl get pods --all-namespaces --selector app.kubernetes.io/name=ingress-nginx is the check the Kubernetes project suggests [1]. Record every annotation in use; that list is your risk register.
  2. Install the CRDs and an implementation. If the platform team has not already done so, install the Standard channel CRDs for the pinned version (v1.7.0, released on 29 October 2026, is the latest at the time of writing [9]), then a conformant implementation [7].
  3. Convert with ingress2gateway [6]. It translates Ingress resources and supported annotations, and warns about what it cannot translate. Version 1.0 raised ingress-nginx annotation coverage from three to "over 30 common annotations" [5].
  4. Review every warning. The release post calls this "the most critical step" [5]. Resolve each warning explicitly: map it, replace it with an implementation policy, or record why it is not needed.
  5. Apply alongside the old Ingress. The Gateway gets its own address. Nothing changes for users yet.
  6. Check status, then compare responses. Wait for Programmed on the Gateway and Accepted plus ResolvedRefs on each route, then send the same requests to both addresses and compare.
  7. Shift traffic in steps. Use weighted DNS or your external load balancer, as the ingress2gateway guidance suggests [5]: for example 5, 25, 50, then 100 per cent, holding at each step long enough to see real traffic.
  8. Bake, then clean up. Keep the Ingress and the old controller running for an agreed period at 100 per cent. Only then delete the Ingress objects and, once nothing else uses it, uninstall the controller [5].
# Step 1: inventory and export
kubectl get ingress -A -o wide
kubectl get ingress -n shop -o yaml > shop-ingress.yaml

# Step 2: Standard channel CRDs (pin the version your platform team approved)
kubectl apply --server-side -f \
  https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.7.0/standard-install.yaml

# Step 3: convert (brew install ingress2gateway, or go install)
ingress2gateway print --providers=ingress-nginx \
  --input-file shop-ingress.yaml > shop-gateway.yaml

# Step 5: apply next to the existing Ingress
kubectl apply -f shop-gateway.yaml

Testing before any user sees it

Status conditions tell you whether the configuration was accepted; they do not tell you it behaves like the old setup. Do both. Route conditions are reported per parent Gateway, with reasons such as NotAllowedByListeners, NoMatchingListenerHostname and BackendNotFound [12].

# Gateway ready, and its address
kubectl -n shop wait gateway/shop-gw --for=condition=Programmed --timeout=120s
GW_IP=$(kubectl -n shop get gateway shop-gw -o jsonpath='{.status.addresses[0].value}')

# Route conditions per parent
kubectl -n shop get httproute shop -o jsonpath=\
'{range .status.parents[*].conditions[*]}{.type}={.status} ({.reason}){"\n"}{end}'

# Old controller address (service name depends on how it was installed)
OLD_IP=$(kubectl -n ingress-nginx get svc ingress-nginx-controller \
  -o jsonpath='{.status.loadBalancer.ingress[0].ip}')

# Same requests, both paths, no DNS change needed
for p in / /api/health /api /API/health /missing; do
  old=$(curl -s -o /dev/null -w '%{http_code}' --resolve shop.example.com:443:$OLD_IP https://shop.example.com$p)
  new=$(curl -s -o /dev/null -w '%{http_code}' --resolve shop.example.com:443:$GW_IP  https://shop.example.com$p)
  echo "$p old=$old new=$new"
done

# HTTP should redirect to HTTPS
curl -s -o /dev/null -w '%{http_code} %{redirect_url}\n' \
  --resolve shop.example.com:80:$GW_IP http://shop.example.com/

Build the path list from real access logs, not from memory: include upper-case variants, trailing slashes, and the regex paths from your annotation inventory. If your load balancer publishes a hostname rather than an IP, resolve it first. Any difference in status codes is a finding to resolve before step 7.

Cutover and rollback

Shifting traffic from the old Ingress controller to the new Gateway. Users on the left send requests to a weighted DNS record or external load balancer. Two paths lead right: the old ingress-nginx controller and the new Gateway, both reaching the same Services. A weight bar below steps through 5, 25, 50 and 100 per cent to the Gateway. A return arrow labelled rollback shows weight can be moved back to the old path at any step while the Ingress objects still exist.
The cutover happens outside the cluster, so each step is a weight change you can reverse in minutes. Rollback stays that cheap only while the Ingress objects and the old controller still exist.

Decide the rollback triggers before you start, and write them into the change ticket.

StageWatchRollback action
Applied, no trafficGateway Programmed, route conditions, response comparisonkubectl delete the new objects; users unaffected
Small share (5 to 25 per cent)4xx and 5xx rates per path on the Gateway versus the old controller; latencySet the Gateway's DNS or LB weight to zero
Majority (50 to 100 per cent)The same, plus redirects, CORS failures in browsers, upload size errorsShift weight back; fix; repeat the step
After clean-upNormal SLOsRe-apply the exported Ingress and reinstall the controller: slow, so delay clean-up

Remember DNS caching. Use a low TTL on the record you are weighting, set well before the migration, or rollback will take as long as your longest client cache.

Common mistakes

  • Applying converter output unread. The ingress2gateway project describes the tool as "a migration assistant, not a one-shot replacement" [5].
  • Testing only the happy path. The differences are in edge cases: case, trailing slashes, regex, encoded characters [4].
  • Forgetting the HTTP listener. Ingress controllers created port 80 implicitly; a Gateway only listens where you declare it [3]. Conversely, ingress2gateway may add an HTTP-to-HTTPS redirect listener you do not want [5].
  • Losing the default backend. Without an explicit PathPrefix / rule, unmatched paths now return 404 [3].
  • Deleting the Ingress on cutover day. That removes your fast rollback.
  • Using Experimental fields by accident. Stick to Standard channel CRDs unless the platform team has agreed otherwise.

Per-service migration checklist

  • Ingress exported to a file and stored with the change.
  • Every annotation listed, with its mapping or a written reason it is dropped.
  • ingress2gateway warnings reviewed and resolved.
  • Gateway Programmed; every route Accepted and ResolvedRefs.
  • Response comparison run against paths taken from real logs, including edge cases.
  • HTTP behaviour (redirect or not) agreed and tested.
  • DNS TTL lowered in advance; weights and hold times written down.
  • Rollback triggers and owner named; Ingress kept until the bake period ends.

What to do this week

  1. Run the ingress-nginx check in every cluster and export all Ingress objects to Git.
  2. Count the annotations in use: kubectl get ingress -A -o json | jq -r '.items[].metadata.annotations // {} | keys[]' | sort | uniq -c | sort -rn.
  3. Run ingress2gateway print against the exports and read every warning.
  4. Pick one low-risk service and complete steps 1 to 6 in a non-production cluster.
  5. Lower the DNS TTL on that service's record so the first real cutover can be reversed quickly.

If you would rather have experienced hands on the cutover, or on-call cover while it bakes, our SysOps 24/7 team runs Kubernetes migrations like this one.

References

  1. Ingress NGINX Retirement: What You Need to Know, Kubernetes blog, 11 November 2025.
  2. Ingress (concept documentation, note on the frozen API), Kubernetes documentation.
  3. Migrating from Ingress, Gateway API guides.
  4. Before You Migrate: Five Surprising Ingress-NGINX Behaviors You Need to Know, Kubernetes blog, 27 February 2026.
  5. Announcing Ingress2Gateway 1.0: Your Path to Gateway API, Kubernetes blog, 20 March 2026.
  6. kubernetes-sigs/ingress2gateway, GitHub.
  7. Getting started with Gateway API (installing the CRDs), Gateway API guides.
  8. HTTP traffic splitting, Gateway API guides.
  9. Gateway API v1.7.0 release notes, GitHub, 29 October 2026.
  10. Gateway API v1.5: Moving features to Stable, Kubernetes blog, 21 April 2026.
  11. HTTPRoute type definitions (filter validation rules), v1.7.0, GitHub.
  12. Route condition types and reasons, v1.7.0, GitHub.