Why we picked Gateway API

Ingress vs Gateway API

Same goal — get external traffic to a pod — but two fundamentally different APIs. Ingress is one resource that bundles everything plus a wall of vendor annotations. Gateway API splits the job across three resources, replaces the annotation wall with typed spec fields, and adds protocols beyond HTTP. Here's the difference, line by line.

1. The resource model

Ingress puts everything into a single object owned by whoever happens to edit it. Gateway API splits the responsibility along the three personas that actually exist in real clusters: cluster admin, platform team, app team.

Ingress one resource

Host, path, TLS, backend, controller-specifics — all in one place. RBAC has nothing to grip on.

Ingress everyone edits this

Gateway API three resources

Each layer is a separate API object. Each persona owns one. RBAC works.

GatewayClass cluster admin
Gateway platform team
HTTPRoute / GRPCRoute / … app team

2. Same use case, both ways

Route /api/v1 to api-v1, do an 80/20 canary between api-v2 and api-v2-canary on /api/v2. Look how the vendor annotations leak into Ingress.

Ingress · NGINX-flavoured 2 objects
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api
  annotations:
    nginx.ingress.kubernetes.io/use-regex: "true"
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /api/v1
        pathType: Prefix
        backend:
          service:
            name: api-v1
            port: { number: 80 }
      - path: /api/v2
        pathType: Prefix
        backend:
          service:
            name: api-v2
            port: { number: 80 }
---
# second Ingress, just to express "20% canary"
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-canary
  annotations:
    nginx.ingress.kubernetes.io/canary: "true"
    nginx.ingress.kubernetes.io/canary-weight: "20"
spec:
  ingressClassName: nginx
  rules:
  - http:
      paths:
      - path: /api/v2
        pathType: Prefix
        backend:
          service:
            name: api-v2-canary
            port: { number: 80 }
Gateway API · any controller 1 object
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: api
spec:
  parentRefs:
  - name: eg
  rules:
  - matches:
    - path: { type: PathPrefix, value: /api/v1 }
    backendRefs:
    - name: api-v1
      port: 80
  - matches:
    - path: { type: PathPrefix, value: /api/v2 }
    backendRefs:
    - name: api-v2
      port: 80
      weight: 80
    - name: api-v2-canary
      port: 80
      weight: 20

# No annotations. The 80/20 split is a typed,
# controller-agnostic field on backendRefs.
# Switching from Envoy Gateway to Cilium
# or NGINX Gateway Fabric: same YAML.

3. What's actually in the spec

Ingress's spec covers host + path + backend. Everything else used to live in annotations: blocks that differ per controller. Gateway API moved the common cases into the spec itself.

Feature Ingress Gateway API
Path matching In spec — Prefix / Exact In spec — PathPrefix / Exact / RegularExpression
Header matching Vendor annotations only In spec — typed headers: on every match
HTTP method matching Not supported In spec — method: on match
Query-param matching Not supported In spec — queryParams: on match
Traffic splitting / canary Vendor annotations — two Ingress objects In spec — weight: on each backendRef
Request mirroring Vendor-specific where supported In spec — RequestMirror filter
URL rewrite Vendor annotations only In spec — URLRewrite filter
Header manipulation Vendor annotations only In spec — RequestHeaderModifier, ResponseHeaderModifier
Protocols HTTP / HTTPS HTTP, gRPC, TLS, TCP, UDP (separate Route kinds)
Cross-namespace routing Same namespace only Explicit allowedRoutes + ReferenceGrant
Standardised status Vendor-specific, often missing Accepted, Programmed, ResolvedRefs on every resource
Persona separation / RBAC One object → one RBAC verb Three resource kinds → three RBAC scopes
Portability across controllers Low — annotations are vendor lock-in High — spec is the contract

4. Where Envoy fits in the timeline

Envoy-based ingress controllers existed long before Gateway API. Each invented its own custom CRDs because Ingress wasn't expressive enough. Gateway API standardises what they all converged on.

2018
Heptio Contour 1.0 ships HTTPProxy: a CRD with header matching, weighted routing, retries — features Ingress wouldn't get for years. Envoy under the hood.
2018
Datawire Ambassador / Emissary ships its own Mapping CRD on top of Envoy. Same motivation: Ingress is too limited.
2019
Solo.io Gloo ships VirtualService CRDs on Envoy. Same pattern again.
2020
SIG-Network starts Service APIs — an effort to unify those custom CRDs into a single upstream spec.
2021
The effort is renamed Gateway API. Ingress is feature-frozen.
Oct 2023
Gateway API v1.0 GAGateway, GatewayClass, HTTPRoute reach Standard channel.
2023→
Envoy Gateway ships as the official Envoy-Foundation implementation: built ground-up against the Gateway API spec, no legacy Ingress baggage.

What's actually running here

The Demo page is served by exactly that stack: a public Open Telekom Cloud ELB hands TLS-encrypted traffic to Envoy Gateway, which evaluates eight upstream Routes (six HTTPRoute, one GRPCRoute, one TCPRoute) and forwards to the matching pod. On top run Envoy-Gateway extension policies — rate limit, CORS, Basic Auth, TLS minimum, security headers and a Coraza WAF — all declarative, no annotations anywhere.

gateway-api v1.2.1 envoy-gateway v1.7.0 k8s v1.33 cce turbo data plane v2 swiss otc