All Articles

Ingress-NGINX Is Being Retired: How to Migrate to Gateway API Before It Breaks

Ingress-NGINX is officially being retired. Your ingress rules will stop working. Here's the step-by-step migration plan to Kubernetes Gateway API before it's too late.

DevOpsBoysMar 21, 20264 min read
Share:Tweet

The announcement is official: Ingress-NGINX is being retired. If you're running it in production — and there's a good chance you are, since it powers over 40% of all Kubernetes ingress traffic — you need a migration plan now.

This isn't a "we'll deprecate it eventually" situation. The maintainers have clearly stated that Ingress-NGINX will receive only critical security patches going forward. No new features. No bug fixes. The future is Gateway API.

Let me show you exactly how to migrate without breaking your traffic.

Why Ingress-NGINX Is Being Retired

The Kubernetes Ingress resource was designed in 2015. It was simple — map a hostname and path to a backend service. But modern traffic routing needs have far outgrown what the Ingress spec can express:

  • No traffic splitting — canary deployments require annotations hacks
  • No header-based routing — need custom NGINX snippets
  • No TCP/UDP support — only HTTP/HTTPS
  • No cross-namespace routing — everything must be in the same namespace
  • Annotation sprawl — every controller invented its own annotation language

Gateway API solves all of these with a proper, extensible specification.

Step 1 — Audit Your Current Ingress Rules

First, find every Ingress resource in your cluster:

bash
kubectl get ingress -A -o wide

Export them for reference:

bash
kubectl get ingress -A -o yaml > ingress-backup.yaml

Categorize your ingresses:

bash
# Count by namespace
kubectl get ingress -A --no-headers | awk '{print $1}' | sort | uniq -c | sort -rn
 
# Check which ones use TLS
kubectl get ingress -A -o jsonpath='{range .items[*]}{.metadata.namespace}/{.metadata.name}: {.spec.tls[*].hosts[*]}{"\n"}{end}'

Document any custom annotations — these are the tricky part of migration:

bash
kubectl get ingress -A -o json | jq -r '.items[].metadata.annotations // {} | keys[]' | sort -u

Step 2 — Install Gateway API CRDs

Gateway API is not installed by default. Install the standard channel CRDs:

bash
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml

Verify installation:

bash
kubectl get crd | grep gateway

You should see:

gatewayclasses.gateway.networking.k8s.io
gateways.gateway.networking.k8s.io
httproutes.gateway.networking.k8s.io
referencegrants.gateway.networking.k8s.io

Step 3 — Choose Your Gateway Controller

You need a controller that implements Gateway API. Popular options:

ControllerBest ForGateway API Support
Envoy GatewayCloud-native, full-featuredFull v1.2
CiliumeBPF performance, no sidecarFull v1.2
NGINX Gateway FabricNGINX familiarityFull v1.2
TraefikExisting Traefik usersFull v1.2
HAProxy Kubernetes IngressHigh-throughput TCPPartial

Install Envoy Gateway (recommended for new setups):

bash
helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.2.0 \
  -n envoy-gateway-system \
  --create-namespace

Step 4 — Create the Gateway Resource

The Gateway replaces your old ingress controller's LoadBalancer service:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: default
spec:
  gatewayClassName: eg  # matches your controller
  listeners:
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: All
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - name: wildcard-tls
        kind: Secret
    allowedRoutes:
      namespaces:
        from: All

Step 5 — Convert Ingress Rules to HTTPRoutes

Here's the translation pattern:

Old Ingress:

yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - host: app.example.com
    http:
      paths:
      - path: /api
        pathType: Prefix
        backend:
          service:
            name: api-service
            port:
              number: 8080

New HTTPRoute:

yaml
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
spec:
  parentRefs:
  - name: main-gateway
  hostnames:
  - "app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /api
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /
    backendRefs:
    - name: api-service
      port: 8080

Step 6 — Handle Common Annotation Migrations

Rate Limiting

yaml
# Old: nginx.ingress.kubernetes.io/limit-rps: "10"
# New: BackendTrafficPolicy (Envoy Gateway)
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: rate-limit
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: my-app
  rateLimit:
    type: Local
    local:
      rules:
      - limit:
          requests: 10
          unit: Second

CORS Headers

yaml
# Old: nginx.ingress.kubernetes.io/enable-cors: "true"
# New: HTTPRoute filter
rules:
- matches:
  - path:
      type: PathPrefix
      value: /api
  filters:
  - type: ResponseHeaderModifier
    responseHeaderModifier:
      add:
      - name: Access-Control-Allow-Origin
        value: "*"

SSL Redirect

yaml
# Old: nginx.ingress.kubernetes.io/ssl-redirect: "true"
# New: HTTPRoute redirect filter
rules:
- matches:
  - path:
      type: PathPrefix
      value: /
  filters:
  - type: RequestRedirect
    requestRedirect:
      scheme: https
      statusCode: 301

Step 7 — Run Both in Parallel

Don't do a big-bang migration. Run both controllers simultaneously:

  1. Deploy Gateway controller alongside Ingress-NGINX
  2. Create HTTPRoutes that mirror your Ingress rules
  3. Update DNS to point to the new Gateway's LoadBalancer IP
  4. Monitor traffic on both controllers
  5. Once Gateway handles all traffic, remove Ingress resources
  6. Uninstall Ingress-NGINX
bash
# Check Gateway's external IP
kubectl get gateway main-gateway -o jsonpath='{.status.addresses[0].value}'
 
# Verify HTTPRoute is attached
kubectl get httproute my-app -o jsonpath='{.status.parents[0].conditions[*].type}'

Migration Checklist

TaskStatus
Export all current Ingress resources
Document custom annotations used
Install Gateway API CRDs
Install Gateway controller
Create Gateway resource with TLS
Convert Ingress rules to HTTPRoutes
Migrate annotation logic to policies
Test with curl/browser against new Gateway
Switch DNS to Gateway LoadBalancer
Monitor for 48 hours
Remove old Ingress resources
Uninstall Ingress-NGINX

Wrapping Up

Ingress-NGINX served us well for years, but its time is up. Gateway API is the official future of Kubernetes traffic routing — it's more powerful, more standardized, and actively maintained.

Don't wait until Ingress-NGINX stops getting security patches. Start your migration now while you can run both in parallel.

Want to master Kubernetes networking, Gateway API, and production traffic management with hands-on labs? The KodeKloud Kubernetes course covers all of this with real cluster environments. If you need a managed Kubernetes cluster to test your migration, DigitalOcean's managed Kubernetes gives you a production-ready cluster in minutes.

Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments