Kubernetes Ingress to Gateway API: A Parallel Cutover Plan
Last edited on August 5, 2026

On March 24, 2026, the ingress-nginx project was retired. Existing controllers did not suddenly stop routing traffic, but retirement ended releases, bug fixes and security patches. That creates a migration deadline without creating permission for a rushed replacement.

Safer migration keeps the current Ingress path intact, brings up a Gateway API implementation on a separate address, translates behavior rather than syntax, and sends controlled requests through both. Shift production traffic only after routes, certificates, redirects, headers, timeouts and application outcomes agree. Keep the old address available during a defined rollback window.

If you can read manifests and use kubectl, this guide provides the practical migration path. It does not prescribe a controller: GatewayClass names, supported features and installation steps depend on the implementation you choose.

Separate the retired controller from the frozen API

Two facts are often compressed into “Ingress is going away,” but they lead to different decisions.

The Kubernetes Ingress API is frozen. Existing Ingress resources and implementations can continue to work, but new networking capabilities are being developed in Gateway API. Separately, ingress-nginx was retired, so an ingress-nginx deployment no longer has a maintained upstream patch path.

Do not delete a working controller merely because replacement manifests render successfully. First record its external address, version, IngressClasses, admission configuration, default backend, TLS Secrets, ConfigMaps and every annotation used by live routes. Capture which namespaces create Ingress resources and which team owns the controller. That inventory is both the translation input and the rollback baseline.

Choose an implementation by supported behavior

Gateway API is a specification, not one universal data plane. Check the implementation’s current Gateway API conformance and release documentation. Confirm support for the resource versions and features you actually need, such as TLS termination, request redirects, header modification, traffic weighting or cross-namespace attachment.

Install the new controller without assigning it the old controller’s address or IngressClass. A separate service address gives each path an observable boundary. If both implementations claim the same address or resources, failed requests no longer tell you which data plane answered.

Map ownership before translating routes

Gateway API divides work that a single Ingress object often mixed together.

GatewayClass selects the controller

Cluster-scoped GatewayClass identifies the controller implementation and normally belongs to platform operators. Application teams should not invent a class name in a route repository and assume the cluster provides it; verify the installed class and its Accepted condition.

Gateway owns listeners and exposure

At the infrastructure attachment point, Gateway defines listeners, ports, protocols, certificate references and which routes may attach. A platform team can expose an HTTPS listener while constraining application routes by namespace or label.

HTTPRoute owns application matching

Application matching belongs in HTTPRoute: hostnames, path or header matches, filters and backend references. Its parentRefs attach it to a Gateway. This typed relationship replaces many annotation-based behaviors, but it does not guarantee that every controller-specific annotation has a portable equivalent.

Start with the smallest route that can reach one existing Service:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: shop
  namespace: storefront
spec:
  parentRefs:
    - name: public-web
      namespace: gateway-system
  hostnames:
    - shop.example.com
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: shop-service
          port: 8080

This manifest proves only the intended relationship. It is not ready for cutover until the Gateway exists, the route is accepted and the application behavior matches the old path.

Convert behavior, not annotation spelling

Annotations may control redirects, rewrites, client-body size, timeouts, retries, affinity, authentication, CORS, snippets or controller-wide features. Classify every annotation into one of four buckets:

  • a portable Gateway API field or standard filter;
  • an implementation-specific extension resource;
  • an application or service-mesh concern that should move out of the edge;
  • unsupported behavior that blocks cutover until redesigned.

The official Ingress2Gateway 1.0 release describes a migration assistant with support for more than 30 common ingress-nginx annotations. It also reports unsupported configuration and expects review. Treat generated YAML as a draft and keep the warnings as migration evidence; automation cannot decide whether a dropped rewrite or timeout is acceptable to your users.

Make implicit defaults explicit

Old behavior may come from the controller ConfigMap or command-line flags rather than the Ingress object. Record default timeouts, maximum request size, forwarded-header trust, real-client-IP handling, access-log format and default certificate. A route conversion that ignores these settings can be syntactically correct and behaviorally different.

Store the translated resources in version control. A self-hosted GitHub Actions runner on a VPS can provide an isolated place for schema validation and policy checks when a managed runner cannot reach the cluster; access to production credentials should remain scoped and short-lived.

Build a second public path without moving users

Create the Gateway with an HTTPS listener and the real certificate reference, but let its Service receive a different external IP or hostname from the old Ingress controller.

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: public-web
  namespace: gateway-system
spec:
  gatewayClassName: your-conformant-class
  listeners:
    - name: https
      protocol: HTTPS
      port: 443
      hostname: "*.example.com"
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            name: wildcard-example-com
      allowedRoutes:
        namespaces:
          from: Selector
          selector:
            matchLabels:
              gateway-access: public

Replace the example class, namespace policy and certificate with values supported by your controller. Label only namespaces that should attach public routes. For a backend in another namespace, require the backend namespace to create a narrowly scoped ReferenceGrant; permission should come from the owner of the referenced object.

Read status before sending traffic

Inspect both the Gateway and HTTPRoute:

kubectl get gateway -n gateway-system public-web -o yaml
kubectl get httproute -n storefront shop -o yaml

Look for listener readiness, an Accepted route condition and ResolvedRefs=True. A false or missing condition is actionable evidence: wrong class, disallowed namespace, missing Service, invalid port or unsupported reference. Even all-green status proves only controller reconciliation, not the customer path.

Probe the new address with the production hostname

Keep public DNS unchanged and direct a test client to the new address while retaining the real Host header and TLS Server Name Indication:

curl --resolve shop.example.com:443:203.0.113.25 \
  -sSvo /dev/null https://shop.example.com/health

Test from outside the cluster as well as inside. Verify the served certificate, chain and hostname. If the manifest points to the expected Secret but the public endpoint serves another certificate, follow Voxfor’s live TLS endpoint mismatch workflow to distinguish controller configuration from DNS, load-balancer or cached-edge behavior.

Prove parity at the response and application layers

Compare evidence from the old and new addresses under the same hostname. Do not reduce the decision to “both returned 200.”

Behavior Test on both paths Cutover evidence
Host and path routing valid host, unknown host, root, nested path and trailing slash same backend and intended error response
Redirects and rewrites HTTP-to-HTTPS, canonical host and rewritten application paths same status, Location and query handling
TLS hostname, issuer, chain and renewal source expected identity and no trust error
Headers and client IP forwarded scheme, host, client address and security headers application receives trusted, bounded values
Request limits representative upload and oversized request documented equivalent response
Timeouts and streaming slow response, WebSocket or stream where used no premature close or unbounded wait
Session behavior repeated authenticated or stateful flow no unexpected login or cart loss
Failure path unavailable backend and missing route intended status, body and alert

Run real application transactions in a controlled environment: login, checkout, API write, upload or webhook, depending on the workload. Compare access logs by request ID and observe error rate, latency and backend selection on each controller.

Client-aborted requests can appear while upstream work continues. If the new path develops a different pattern of NGINX-style 499 responses, work through Voxfor’s 499 and upstream-latency diagnostic to separate a client timeout from the underlying slow operation. Do not copy its NGINX tuning blindly into a different controller; use it to reason about timing evidence.

Shift traffic in bounded stages

A migration is ready when both endpoints are independently addressable, parity checks pass, observability distinguishes them and rollback has been rehearsed.

  1. Freeze Ingress and Gateway route changes for the cutover window.
  2. Record old and new addresses, manifest revisions, controller versions, certificate identity and baseline metrics.
  3. Lower DNS TTL ahead of time if DNS will be the switch, while acknowledging that caches and existing connections may outlive it.
  4. Send a small, identifiable traffic cohort to the new endpoint through a load balancer, weighted DNS or a controlled client population.
  5. Compare response codes, latency, application outcomes, logs and saturation for a declared observation period.
  6. Increase the share only when the current stage passes its acceptance thresholds.
  7. Move the default path, then keep watching both addresses through the rollback window.

Do not use replica restarts or node maintenance as an accidental cutover mechanism. Traffic migration and workload mobility are separate controls. When nodes also need maintenance, consult Voxfor’s Kubernetes node-drain readiness guide for PodDisruptionBudget and eviction evidence.

Watch the platform beneath the routers. Image pulls, log growth or temporary duplicate traffic can expose storage pressure during the overlap. If a node reports DiskPressure, follow Voxfor’s nodefs, imagefs and containerfs recovery path before assuming Gateway API caused the application failure.

Roll back traffic, not just manifests

Define rollback as a traffic action with an owner and time limit. If error rate, latency, TLS identity, session outcomes or critical transactions cross a threshold, return new traffic to the old external address while the old controller and routes remain unchanged.

Reverting an HTTPRoute commit is not enough when DNS or an upstream load balancer still sends users to the Gateway address. Restore the actual routing control, confirm the old endpoint receives new requests, and preserve the failed Gateway logs and manifests for diagnosis. Long-lived connections and cached DNS may drain gradually, so continue observing both data planes.

If the new controller caused no persistent application write difference, rollback is mainly a routing change. If requests created orders, jobs or other durable state before failing, reconcile that state at the application layer; traffic reversal does not undo completed business operations.

Decommission only after the return path expires

Keep the old controller through a declared stability period that covers peak traffic, certificate activity, scheduled jobs and the important client types. Remove obsolete Ingress resources only after the Gateway route owns production traffic and no rollback depends on them.

Then remove ingress-nginx in a controlled maintenance change. Confirm that no IngressClass, admission webhook, Service, ConfigMap, namespace or monitoring rule is still required. Search all namespaces for remaining Ingress objects and annotations before deleting cluster-wide components.

Completion evidence should include the final Gateway and route conditions, external TLS result, application transaction, traffic-controller change, observation window, rollback expiry and proof that the retired controller no longer receives requests.

FAQ: Ingress to Gateway API migration

Is Kubernetes Ingress removed?

No. The Ingress API is frozen, which means it remains available but is not where new networking features are being developed. The ingress-nginx project is a separate implementation and was retired on March 24, 2026, ending upstream releases and security patches.

Can Ingress and Gateway API run at the same time?

Yes, when separate controllers own separate resources and external addresses. Parallel operation lets you test the Gateway path without moving all users. Avoid ambiguous ownership of the same address, class or load balancer.

Does Ingress2Gateway convert every ingress-nginx annotation?

No. It can translate many common configurations and report warnings, but unsupported or implementation-specific behavior still needs review. Validate the generated resources against the selected controller and test the resulting behavior.

What proves an HTTPRoute is ready?

Accepted=True and ResolvedRefs=True show that the controller accepted the relationship and resolved referenced objects. Production readiness also requires external TLS, routing, headers, redirect, limit, timeout, session, failure-path and application tests.

How should I test a Gateway before changing DNS?

Give the Gateway a separate address and send requests to it with the real hostname and TLS SNI, for example with curl --resolve. Test from outside the cluster and compare the old and new endpoints using the same request set.

What is the fastest rollback during cutover?

Return traffic to the old external address while the old controller remains intact. The exact control may be a load balancer, DNS record or client cohort rule. Reverting YAML alone does not reverse an already changed traffic source.

When can ingress-nginx be removed?

Remove it after the Gateway path has survived the defined stability window, rollback has expired, all remaining Ingress objects and dependencies are accounted for, and monitoring shows the old controller receives no traffic.

Close with evidence from both paths

The useful migration record is short and reproducible: old and new controller versions, separate addresses, route conditions, certificate result, parity test set, traffic stages, rollback threshold and decommission date. Keep it with the route manifests.

Gateway API adoption succeeds when the new data plane owns production traffic by evidence—not when converted YAML merely applies. For more adjacent deployment and operations work, continue through Voxfor’s DevOps guides.

Leave a Reply

Your email address will not be published. Required fields are marked *