Ingress NGINX Deprecation: Complete Migration Guide to Kubernetes Gateway API

In January 2026, the Kubernetes Steering and Security Response Committees issued a critical warning: Ingress NGINX will cease receiving security patches from March 2026. With approximately 50% of cloud-native environments relying on this controller, this deprecation represents one of the most significant infrastructure migrations in Kubernetes history. This comprehensive guide provides a step-by-step migration path from Ingress NGINX to the Kubernetes Gateway API, including traffic management patterns, TLS configuration, and enterprise deployment strategies.

⚠️
CRITICAL SECURITY NOTICE

Ingress NGINX will stop receiving security patches in March 2026. Any vulnerabilities discovered after this date will remain unpatched. Begin migration immediately to avoid exposure to zero-day exploits.

Understanding the Deprecation Timeline

The Ingress NGINX project has been in maintenance mode since late 2024, with the Kubernetes community focusing resources on the Gateway API as the future standard for ingress traffic management. The deprecation follows a clear timeline:

DateMilestoneAction Required
January 2026Official deprecation announcementBegin migration planning
March 2026Security patches ceaseComplete migration or accept risk
June 2026Repository archivedNo further updates of any kind

Why Gateway API is the Future

The Kubernetes Gateway API is not merely a replacement for Ingress—it’s a complete rethinking of how traffic enters and routes through Kubernetes clusters. Key advantages include:

Role-Based Resource Model

Gateway API separates concerns across three personas:

graph TB
    subgraph InfraTeam ["Infrastructure Team"]
        GC[GatewayClass]
    end
    
    subgraph PlatformTeam ["Platform Team"]
        GW[Gateway]
    end
    
    subgraph AppTeam ["Application Team"]
        HR[HTTPRoute]
        GR[GRPCRoute]
        TR[TCPRoute]
    end
    
    GC --> GW
    GW --> HR
    GW --> GR
    GW --> TR
    
    style GC fill:#E3F2FD,stroke:#1565C0
    style GW fill:#E8F5E9,stroke:#2E7D32
    style HR fill:#FFF3E0,stroke:#EF6C00
    style GR fill:#FFF3E0,stroke:#EF6C00
    style TR fill:#FFF3E0,stroke:#EF6C00
  • GatewayClass (Infrastructure): Defines which controller handles traffic (e.g., Envoy, Cilium, NGINX Gateway Fabric)
  • Gateway (Platform): Configures listeners, TLS certificates, and allowed route types
  • HTTPRoute/GRPCRoute (Application): Defines routing rules, path matching, and backend services

Feature Comparison

FeatureIngressGateway API
Header-based routingAnnotations (non-standard)Native support
Traffic splittingNot supportedNative weights
Cross-namespace routingComplex workaroundsReferenceGrant
gRPC routingAnnotationsNative GRPCRoute
TCP/UDP routingSeparate CRDsNative TCPRoute/UDPRoute
Request/response modificationAnnotationsNative filters

Choosing a Gateway API Implementation

Unlike Ingress (where NGINX dominated), Gateway API has multiple mature implementations. Each has distinct strengths:

ImplementationBest ForKey Features
Envoy GatewayGeneral purpose, familiar to Istio usersFull Gateway API conformance, extensible via EnvoyPatchPolicy
Cilium GatewayeBPF-native networking, high performanceKernel-level routing, no sidecar required
NGINX Gateway FabricTeams with NGINX expertiseFamiliar configuration model, commercial support
TraefikDeveloper-friendly, Let’s Encrypt integrationDashboard, automatic TLS, middleware
Kong GatewayAPI management integrationPlugin ecosystem, rate limiting, auth
💡
RECOMMENDATION

For most enterprises migrating from Ingress NGINX, we recommend Envoy Gateway due to its full Gateway API conformance and extensibility, or NGINX Gateway Fabric if your team has deep NGINX operational expertise.

Step-by-Step Migration Guide

Step 1: Install Gateway API CRDs

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

# Install Gateway API v1.2 CRDs (January 2026 stable)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.2.0/standard-install.yaml

# Verify installation
kubectl get crd | grep gateway
# Expected output:
# gatewayclasses.gateway.networking.k8s.io
# gateways.gateway.networking.k8s.io
# httproutes.gateway.networking.k8s.io
# referencegrants.gateway.networking.k8s.io

Step 2: Deploy a Gateway Controller

Example using Envoy Gateway:

# Install Envoy Gateway via Helm
helm install envoy-gateway oci://docker.io/envoyproxy/gateway-helm   --version v1.2.0   --namespace envoy-gateway-system   --create-namespace

# Verify the GatewayClass is available
kubectl get gatewayclass
# NAME           CONTROLLER                        ACCEPTED
# envoy-gateway  gateway.envoyproxy.io/gateway     True

Step 3: Create a Gateway Resource

The Gateway defines listeners (ports, protocols, TLS) and replaces the ingress controller’s global configuration:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: production-gateway
  namespace: gateway-system
spec:
  gatewayClassName: envoy-gateway
  listeners:
  - name: https
    protocol: HTTPS
    port: 443
    tls:
      mode: Terminate
      certificateRefs:
      - kind: Secret
        name: wildcard-tls
        namespace: gateway-system
    allowedRoutes:
      namespaces:
        from: Selector
        selector:
          matchLabels:
            gateway-access: "true"
  - name: http
    protocol: HTTP
    port: 80
    allowedRoutes:
      namespaces:
        from: Same

Step 4: Migrate Ingress Rules to HTTPRoute

Here’s a side-by-side comparison of Ingress vs HTTPRoute:

# BEFORE: Ingress NGINX
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
  - hosts:
    - api.example.com
    secretName: api-tls
  rules:
  - host: api.example.com
    http:
      paths:
      - path: /v1
        pathType: Prefix
        backend:
          service:
            name: api-v1
            port:
              number: 80
      - path: /v2
        pathType: Prefix
        backend:
          service:
            name: api-v2
            port:
              number: 80
# AFTER: Gateway API HTTPRoute
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-routes
  namespace: api-namespace
spec:
  parentRefs:
  - name: production-gateway
    namespace: gateway-system
  hostnames:
  - "api.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /v1
    backendRefs:
    - name: api-v1
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /v2
    backendRefs:
    - name: api-v2
      port: 80

Step 5: Implement Traffic Splitting (Canary Deployments)

One of Gateway API’s killer features is native traffic splitting:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-deployment
spec:
  parentRefs:
  - name: production-gateway
    namespace: gateway-system
  hostnames:
  - "app.example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /
    backendRefs:
    - name: app-stable
      port: 80
      weight: 90    # 90% to stable
    - name: app-canary
      port: 80
      weight: 10    # 10% to canary

Step 6: Cross-Namespace Routing with ReferenceGrant

To allow routes in one namespace to reference services in another:

# In the target namespace (backend-services)
apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-gateway-routes
  namespace: backend-services
spec:
  from:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    namespace: frontend-apps
  to:
  - group: ""
    kind: Service

Migration Architecture Pattern

For zero-downtime migration, run both controllers in parallel during the transition:

graph LR
    subgraph External ["External Traffic"]
        LB["Cloud Load Balancer"]
    end
    
    subgraph Cluster ["Kubernetes Cluster"]
        NGINX["Ingress NGINX (Legacy)"]
        GW["Gateway API (New)"]
        SVC1["Service A"]
        SVC2["Service B"]
        SVC3["Service C"]
    end
    
    LB --> NGINX
    LB --> GW
    NGINX --> SVC1
    NGINX --> SVC2
    GW --> SVC2
    GW --> SVC3
    
    style NGINX fill:#FFCDD2,stroke:#C62828
    style GW fill:#C8E6C9,stroke:#2E7D32

Migration phases:

  • Phase 1: Deploy Gateway API controller alongside Ingress NGINX
  • Phase 2: Migrate low-risk services to HTTPRoute (both controllers serve traffic)
  • Phase 3: Migrate remaining services, validate with synthetic traffic
  • Phase 4: Update DNS/Load Balancer to point only to Gateway
  • Phase 5: Decommission Ingress NGINX controller

Common Migration Challenges

Challenge 1: Annotation-Heavy Configurations

Ingress NGINX uses annotations for features like rate limiting, CORS, and authentication. Gateway API uses Filters and Policies:

# Request header modification (replaces rewrite annotations)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
spec:
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /legacy
    filters:
    - type: URLRewrite
      urlRewrite:
        path:
          type: ReplacePrefixMatch
          replacePrefixMatch: /api
    - type: RequestHeaderModifier
      requestHeaderModifier:
        add:
        - name: X-Forwarded-Prefix
          value: /legacy
    backendRefs:
    - name: api-service
      port: 80

Challenge 2: Custom Error Pages

Use the controller’s extension mechanisms. For Envoy Gateway:

apiVersion: gateway.envoyproxy.io/v1alpha1
kind: BackendTrafficPolicy
metadata:
  name: custom-error-responses
spec:
  targetRefs:
  - group: gateway.networking.k8s.io
    kind: HTTPRoute
    name: api-routes
  responseOverride:
    rules:
    - match:
        statusCodes:
        - value: 503
      response:
        contentType: application/json
        body:
          type: Inline
          inline: '{"error": "Service temporarily unavailable"}'

Validation and Testing

# Validate HTTPRoute status
kubectl get httproute api-routes -o jsonpath='{.status.parents[*].conditions}'

# Test with curl through the new gateway
curl -H "Host: api.example.com" http://GATEWAY_IP/v1/health

# Compare responses between old and new paths
diff <(curl -s http://OLD_NGINX_IP/v1/users) <(curl -s http://GATEWAY_IP/v1/users)

Key Takeaways

  • Ingress NGINX security patches end March 2026—migration is not optional for security-conscious organizations.
  • Gateway API is the Kubernetes-native replacement with superior features: traffic splitting, cross-namespace routing, and role-based resource ownership.
  • Run controllers in parallel during migration to enable gradual, zero-downtime cutover.
  • Choose your implementation based on team expertise: Envoy Gateway for flexibility, NGINX Gateway Fabric for familiarity.
  • Plan for annotation translation—most Ingress NGINX annotations have Gateway API equivalents via Filters or controller-specific policies.

Conclusion

The deprecation of Ingress NGINX marks the end of an era, but Gateway API represents a significant improvement in how Kubernetes handles ingress traffic. Its role-based model, native traffic management features, and growing ecosystem make it the clear path forward. Start your migration now—waiting until March puts your infrastructure at risk of unpatched vulnerabilities. Use the parallel-running strategy outlined in this guide to minimize disruption and validate thoroughly before final cutover.

References


Discover more from C4: Container, Code, Cloud & Context

Subscribe to get the latest posts sent to your email.

Leave a comment

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.