Kubernetes Gateway API: Migrating from Nginx Ingress
Kubernetes & Conteneurs

Kubernetes Gateway API: Migrating from Nginx Ingress

March 27, 20268 min readKubernetesGateway APIIngress

The Gateway API became GA in Kubernetes 1.31 and is gradually replacing the Ingress. More expressive, multi-tenant and extensible — here is how to migrate your workloads.

Why Ingress is Limited

The Kubernetes Ingress object was designed in 2015 for a simple use case: exposing HTTP services behind a reverse proxy. In 2026, it shows its limitations in several dimensions:

  • Zero portability: advanced routing (header-based, weight-based) is implemented via proprietary annotations (nginx.ingress.kubernetes.io/*) that are not portable between implementations
  • Single-tenant: a single namespace controls the configuration of all routing rules
  • Limited extensibility: impossible to express TCP/UDP routing or timeout policies natively
  • Tight coupling: load balancer configuration (gateway) is mixed with routing rules (routes)

Gateway API: Key Resources

The Gateway API introduces a hierarchy of resources that cleanly separates responsibilities:

  • GatewayClass: defines the type of implementation (Contour, Cilium, Envoy Gateway). Cluster-scoped resource managed by the admin.
  • Gateway: instance of a load balancer with its listeners (ports, protocols, TLS). Managed by the infrastructure team.
  • HTTPRoute: HTTP routing rules attached to one or more Gateways. Managed by application teams in their own namespaces.
  • TCPRoute / TLSRoute: TCP and TLS passthrough routing for non-HTTP workloads.
  • ReferenceGrant: explicit authorisation for an HTTPRoute in one namespace to access a Service in another namespace.

Available Implementations

Several Gateway API implementations are available in production in 2026:

  • Envoy Gateway: maintained by the CNCF, based on Envoy, recommended for new projects
  • Cilium: native eBPF integration, excellent performance, ideal on EKS
  • Contour: based on Envoy, proven maturity, VMware support
  • Kong: rich API management features
  • nginx-gateway-fabric: official NGINX implementation of the Gateway API

HTTPRoute Examples

Path-Based Routing

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api-routes
  namespace: backend
spec:
  parentRefs:
    - name: prod-gateway
      namespace: infrastructure
  hostnames:
    - "api.move2cloud.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /v1/payments
      backendRefs:
        - name: payment-service
          port: 8080
    - matches:
        - path:
            type: PathPrefix
            value: /v1/users
      backendRefs:
        - name: user-service
          port: 8080

Header-Based Routing (A/B Testing)

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: ab-test
  namespace: frontend
spec:
  parentRefs:
    - name: prod-gateway
      namespace: infrastructure
  rules:
    - matches:
        - headers:
            - name: X-Beta-User
              value: "true"
      backendRefs:
        - name: frontend-v2
          port: 3000
    - backendRefs:
        - name: frontend-v1
          port: 3000

Canary with Weight-Based Routing

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-release
  namespace: backend
spec:
  parentRefs:
    - name: prod-gateway
      namespace: infrastructure
  rules:
    - backendRefs:
        - name: my-service-v1
          port: 8080
          weight: 90    # 90 % of traffic to v1
        - name: my-service-v2
          port: 8080
          weight: 10    # 10 % of traffic to v2

Migration from Nginx Ingress

Here is how to convert a typical NGINX Ingress rule to an HTTPRoute:

# BEFORE: NGINX Ingress with proprietary annotations
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: my-app
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
    nginx.ingress.kubernetes.io/rate-limit: "100"
spec:
  ingressClassName: nginx
  rules:
    - host: app.move2cloud.com
      http:
        paths:
          - path: /api
            pathType: Prefix
            backend:
              service:
                name: api-service
                port:
                  number: 8080
---
# AFTER: HTTPRoute Gateway API (portable, standardised)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: my-app
  namespace: production
spec:
  parentRefs:
    - name: prod-gateway
      namespace: infrastructure
  hostnames:
    - "app.move2cloud.com"
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: api-service
          port: 8080

Multi-Tenancy with ReferenceGrant

The ReferenceGrant is a security mechanism that explicitly authorises an HTTPRoute in one namespace to reference a Service in another namespace:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-team-a-to-backend
  namespace: backend        # Namespace of the target Service
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: team-a     # Authorised source namespace
  to:
    - group: ""
      kind: Service

TLS with cert-manager

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: prod-gateway
  namespace: infrastructure
  annotations:
    cert-manager.io/cluster-issuer: letsencrypt-prod
spec:
  gatewayClassName: cilium
  listeners:
    - name: https
      port: 443
      protocol: HTTPS
      hostname: "*.move2cloud.com"
      tls:
        mode: Terminate
        certificateRefs:
          - name: wildcard-tls-cert
            namespace: infrastructure

Conclusion

The Gateway API is the direction Kubernetes is heading for managing inbound traffic. It resolves the fundamental problems of Ingress: portability, multi-tenancy, expressiveness. Migration is gradual — you can run Ingress and the Gateway API side by side during the transition. Start with new services and migrate the old ones progressively.

← Back to blog