LearnKubernetes
Kubernetes9. Networking·ADVANCED·6 min read

Ingress to Gateway API Migration

Ingress to Gateway API Migration: A Practical Strategy

TL;DR

  • Ingress and Gateway API can coexist in the same cluster; partition ownership by hostname/path to avoid conflicts.
  • ingress2gateway tool auto-converts Ingress → HTTPRoute (basic rules, hosts, paths, TLS), but NOT vendor-specific annotations.
  • Migration strategy: incremental (route-by-route), not big-bang — limits blast radius if conversion breaks.
  • Test thoroughly: PathPrefix behavior edge cases (trailing slashes) may differ between controllers; validate converted routes.
  • Keep both controllers running until full Ingress→HTTPRoute conversion; then decommission Ingress Controller only after kubectl get ingress -A returns empty.

Core Concepts

Safe Coexistence Model

Problem: both controllers listening on same hostname = ambiguous routing.

Solution: partition ownership at load-balancer/DNS level:

  • Phase 1: Ingress owns api-v1.example.com, Gateway owns api-v2.example.com
  • Phase 2: update DNS to point api.example.com → Gateway
  • Phase 3: decommission Ingress Controller

ingress2gateway Tool Output

Converts automatically:

  • Host / path rules → HTTPRoute rules
  • TLS blocks → HTTPRoute TLS config
  • Service references

Does NOT convert:

  • Vendor annotations (NGINX: rewrite-target, rate-limit; GCE: load-balancer-policy)
  • Canary / traffic splitting logic
  • Custom auth / WAF policies

Manual work required: re-apply vendor features using HTTPRoute filters (e.g., RequestHeaderModifier, URLRewrite).

Migration Workflow

  1. Audit annotations:

    kubectl get ingress -A -o json | jq -r '.items[].metadata.annotations | keys[]' | sort -u
  2. Plan equivalents: map each annotation to Gateway API filter or controller extension

  3. Set up Gateway: create GatewayClass + Gateway (platform team)

  4. Incremental conversion:

    • Pick lowest-risk Ingress (fewest vendor features)
    • Run ingress2gateway on it
    • Manually re-apply vendor logic as filters
    • Test (curl with Host header)
    • Deploy HTTPRoute alongside Ingress
    • Monitor both, then remove Ingress
  5. Repeat: next Ingress → HTTPRoute

Configuration & Command Reference

Inventory all annotations:

kubectl get ingress -A -o json | jq '.items[] | {name, annotations: .metadata.annotations}'

Convert single Ingress:

ingress2gateway print --input-file=payments-ingress.yaml

Test converted HTTPRoute:

kubectl apply -f converted-httproute.yaml
kubectl describe httproute <name>  # Check parentRef attachment
curl -H "Host: api.example.com" http://<gateway-ip>/api/health

Verify no Ingress remains before decommissioning controller:

kubectl get ingress -A  # Must return empty

Common Pitfalls

  • Pitfall: Converted Ingress → HTTPRoute, routes don’t attach (parentRef shows not attached). Why: Gateway not listening on route’s hostname, or HTTPRoute namespace not allowed by Gateway’s allowedRoutes. Fix: kubectl describe httproute shows attachment errors; verify Gateway listeners[].hostnames includes route’s hostname.

  • Pitfall: PathPrefix matching changed (e.g., /api now matches /apiextra when it shouldn’t, or vice versa). Why: controller-specific Prefix behavior differs (some honor slash-delimited boundaries, others don’t). Fix: test each converted route in staging; use exact path matching if Prefix behavior differs from original controller.

  • Pitfall: Vendor features (NGINX rewrite, canary weight) lost after conversion; routes work but behavior differs. Why: ingress2gateway doesn’t convert annotations; manual re-implementation required. Fix: map annotation to HTTPRoute filter; if filter doesn’t exist, use controller-specific extension or CRD.

Interview Questions

  • Why is incremental migration safer than big-bang, and what specific risk does it reduce? — Tests blast radius awareness.

  • An HTTPRoute was converted from Ingress but doesn’t route traffic. What should you check first? — Tests troubleshooting approach.

  • Your Ingress uses NGINX annotations not supported by your Gateway controller. How do you handle this? — Tests understanding of feature parity gaps.


This handles the mechanical translation of paths, hosts, and TLS blocks reliably, but **vendor-specific annotations are not automatically converted** — those require manual translation to the equivalent HTTPRoute `filters` or controller-specific extension resources.

---

## 3. Incremental, Route-by-Route Cutover

Phase 1: Deploy Gateway + GatewayClass alongside existing Ingress Controller Phase 2: Migrate ONE low-risk route to HTTPRoute, validate in production Phase 3: Migrate remaining routes incrementally, in order of increasing criticality Phase 4: Decommission the old Ingress Controller once zero Ingress objects remain


During the overlap period, both an Ingress Controller and a Gateway API controller can run simultaneously, each serving different hostnames/paths — DNS or a load balancer in front decides which system actually receives traffic for a given route during the transition.

```yaml
# A converted HTTPRoute equivalent to a simple path-based Ingress rule
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: payments-migrated
  namespace: payments
spec:
  parentRefs:
  - name: shared-gateway
    namespace: infra
  hostnames: ["payments.example.com"]
  rules:
  - matches:
    - path: {type: PathPrefix, value: /api}
    backendRefs:
    - name: payments-api
      port: 8080

4. Hands-on Command Cheat Sheet

Run the conversion tool against live cluster Ingress objects directly:

ingress2gateway print --namespace=payments > payments-gateway-api.yaml

Compare the behavior of both objects side by side during the overlap window:

kubectl describe ingress payments-ingress -n payments
kubectl describe httproute payments-migrated -n payments

Verify traffic is actually reaching the new HTTPRoute path (test directly, bypassing DNS if still pointed at the old Ingress):

curl -H "Host: payments.example.com" http://<new-gateway-ip>/api/health

Confirm zero remaining Ingress objects before decommissioning the old controller:

kubectl get ingress -A

5. Architectural Interview Q&A

Q: Why shouldn’t a team attempt a single big-bang cutover from Ingress to Gateway API across an entire production cluster in one change window? A: Vendor-specific Ingress annotations (rewrite rules, custom auth, rate limiting, canary weights) don’t have a fully automatic translation to Gateway API — each one requires manual verification that the equivalent HTTPRoute filter or extension actually produces identical behavior, and subtle behavioral differences (default path-matching semantics, header case sensitivity) are easy to miss until real production traffic exercises them. An incremental, route-by-route migration limits the blast radius of any single translation mistake to one route rather than every route in the cluster simultaneously.

Q: During the overlap period with both an Ingress Controller and a Gateway API controller running, how do you avoid routing conflicts for the same hostname? A: Explicitly partition ownership at the DNS/load-balancer layer — each hostname (or path prefix, if the front-end load balancer supports path-based routing to different backend controller IPs) is directed to exactly one controller at a time, never both simultaneously for the same traffic. Only after a specific hostname’s HTTPRoute is validated and confirmed correct should the DNS record (or LB rule) actually be flipped to send that traffic to the new Gateway API controller instead of the old Ingress Controller.


6. Common Pitfalls

[!WARNING] Assuming Path-Matching Semantics Are Identical Ingress’s pathType: Prefix and Gateway API’s PathPrefix match type look similar but have historically had subtle edge-case differences in trailing-slash handling and exact-boundary matching depending on the specific controller implementation on each side. Never assume a converted route behaves byte-for-byte identically without explicit testing against real request patterns (including edge cases like trailing slashes, empty paths, and query strings) — this is exactly the kind of subtle regression that ingress2gateway’s mechanical conversion cannot catch for you.

🔒 Locked
Sign in and unlock the full syllabus to keep reading.
Sign in to continue
Knowledge check
5 scenario questions on this topic
Take the quiz →
Related in 9. Networking
DNS Fundamentals
INTERMEDIATE
CoreDNS
EXPERT
Kubernetes Networking
EXPERT
Ingress
EXPERT