Agent Factory Archive

Gateway API - The New Standard

Learn Gateway API as the Kubernetes standard replacing Ingress with role separation and portability

Status
stable
Trust
unverified
Approved
ag-archive/0.1.0 ·
9 min read

The previous lesson showed Traefik's simpler approach—IngressRoute CRDs that replace Ingress annotations with structured YAML. You could route traffic in minutes.

But Traefik's CRDs are Traefik-only. Switch to Envoy Gateway, and you rewrite everything. Switch to Istio, rewrite again. The Kubernetes community recognized this problem in 2020 and began designing a solution: Gateway API.

Gateway API is not another proprietary alternative. It's the official Kubernetes standard for traffic routing, designed to replace Ingress entirely. In October 2023, Gateway API reached General Availability (GA). Major implementations—Envoy Gateway, Istio, Traefik, Kong—all support it. Your Gateway API configurations work across any of them.

This lesson teaches the specification first. You'll understand the conceptual model before writing YAML. When you see the resources, you'll know why they exist.


The Problem Gateway API Solves

Ingress created two problems that couldn't be fixed with patches:

Problem 1: Annotation chaos. Every feature beyond basic routing required vendor-specific annotations. Rate limiting in NGINX Ingress used different syntax than Traefik Ingress, which differed from Kong Ingress. Switching vendors meant rewriting every configuration.

Problem 2: No role separation. In production, different teams own different responsibilities. The platform team manages infrastructure. The security team manages TLS policies. Application developers define routes. Ingress forced everyone to edit the same resource. A developer adding a route could accidentally modify TLS configuration.

Gateway API solves both problems with a new architecture: three tiers of resources, each owned by different personas.


The Three-Tier Resource Model

Gateway API organizes traffic configuration into three resource types, each at a different scope and owned by different personas.

┌─────────────────────────────────────────────────────────────────────────┐
│                    TIER 1: INFRASTRUCTURE                                │
│                    Owner: Cluster Administrator                          │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │ GatewayClass                                                       │  │
│  │   - Cluster-scoped (applies to entire cluster)                     │  │
│  │   - Defines WHICH CONTROLLER implements Gateways                   │  │
│  │   - Similar to StorageClass, IngressClass                          │  │
│  │   - Example: "eg" for Envoy Gateway, "traefik" for Traefik         │  │
│  └───────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                    TIER 2: PLATFORM                                      │
│                    Owner: Platform Team                                  │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │ Gateway                                                            │  │
│  │   - Namespace-scoped                                               │  │
│  │   - Defines ENTRY POINTS for traffic (listeners)                   │  │
│  │   - Configures ports, protocols, TLS settings                      │  │
│  │   - Controls which namespaces can attach routes                    │  │
│  └───────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────────────┐
│                    TIER 3: APPLICATION                                   │
│                    Owner: Application Developer                          │
│  ┌───────────────────────────────────────────────────────────────────┐  │
│  │ HTTPRoute / GRPCRoute / TCPRoute / TLSRoute / UDPRoute             │  │
│  │   - Namespace-scoped                                               │  │
│  │   - Defines ROUTING RULES (paths, headers, backends)               │  │
│  │   - Attaches to a Gateway via parentRefs                           │  │
│  │   - Points to backend Services                                     │  │
│  └───────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────┘

This separation enables RBAC at the resource level. Cluster administrators create GatewayClasses. Platform teams create Gateways. Application developers create Routes. Each team has permissions for their tier only.


GatewayClass: Infrastructure Definition

GatewayClass is the top of the hierarchy. It defines which controller handles Gateways that reference it.

Think of GatewayClass like StorageClass for persistent volumes:

  • StorageClass defines which storage provisioner creates PersistentVolumes
  • GatewayClass defines which traffic controller implements Gateways

GatewayClass is cluster-scoped—it applies to the entire cluster, not a single namespace.

GatewayClass Resource

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: eg
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

Output:

gatewayclass.gateway.networking.k8s.io/eg created

The key field is controllerName. This string tells Kubernetes which controller should reconcile Gateways using this class. Envoy Gateway uses gateway.envoyproxy.io/gatewayclass-controller. Traefik uses traefik.io/gateway-controller. Istio uses istio.io/gateway-controller.

Who Creates GatewayClass?

Usually, you don't create GatewayClasses. The controller's Helm chart creates them automatically when you install it.

Check existing GatewayClasses in your cluster:

kubectl get gatewayclasses

Output:

NAME      CONTROLLER                                      ACCEPTED   AGE
eg        gateway.envoyproxy.io/gatewayclass-controller   True       2d
traefik   traefik.io/gateway-controller                   True       5d

If you installed Envoy Gateway, the eg class exists. If you installed Traefik with Gateway API support, the traefik class exists. You choose which class to use when creating Gateways.

Multiple GatewayClasses

A cluster can have multiple GatewayClasses. This enables scenarios like:

GatewayClassUse Case
eg (Envoy Gateway)Production traffic with advanced features
traefikDevelopment/staging with simpler config
istioService mesh with mutual TLS

Application teams choose the appropriate class for their workload by referencing it in their Gateway.


Gateway: Traffic Entry Point

Gateway defines where traffic enters the cluster. It's the specification for an actual load balancer or proxy.

Gateway is namespace-scoped—it lives in a specific namespace and can be managed by the team owning that namespace.

Gateway Resource

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: task-api-gateway
  namespace: task-api
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80

Output:

gateway.gateway.networking.k8s.io/task-api-gateway created

Understanding Listeners

Listeners define what the Gateway accepts:

FieldPurpose
nameIdentifier for this listener (referenced by Routes)
protocolHTTP, HTTPS, TLS, TCP, or UDP
portNetwork port to listen on
hostnameOptional: limit to specific hostname

A single Gateway can have multiple listeners:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: main-gateway
  namespace: gateway-system
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - name: tls-secret

Output:

gateway.gateway.networking.k8s.io/main-gateway created

This Gateway accepts HTTP on port 80 and HTTPS on port 443, terminating TLS with the certificate from tls-secret.

Checking Gateway Status

Unlike Ingress, Gateway API provides rich status reporting:

kubectl get gateway task-api-gateway -n task-api -o yaml

Output:

status:
  addresses:
    - type: IPAddress
      value: 10.96.123.45
  conditions:
    - type: Accepted
      status: "True"
      reason: Accepted
      message: Gateway accepted by controller
    - type: Programmed
      status: "True"
      reason: Programmed
      message: Gateway programmed successfully
  listeners:
    - name: http
      attachedRoutes: 1
      conditions:
        - type: Accepted
          status: "True"

The status tells you:

  • addresses: Where traffic is received (external IP or hostname)
  • conditions: Whether the Gateway is accepted and programmed
  • listeners: How many routes are attached to each listener

Compare this to Ingress, where status was often empty or inconsistent across controllers.


HTTPRoute: Application Routing

HTTPRoute defines how HTTP requests are routed to backend Services. This is the resource application developers create.

HTTPRoute is namespace-scoped—developers create routes in their application's namespace.

HTTPRoute Resource

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: task-api-route
  namespace: task-api
spec:
  parentRefs:
    - name: task-api-gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api/v1/tasks
      backendRefs:
        - name: task-api
          port: 8000

Output:

httproute.gateway.networking.k8s.io/task-api-route created

Understanding the Structure

SectionPurpose
parentRefsWhich Gateway(s) this route attaches to
rulesRouting rules (matches + backends)
matchesConditions that must be true (path, headers, etc.)
backendRefsWhere to send matched requests

Path Matching

Gateway API supports multiple path match types:

matches:
  - path:
      type: Exact
      value: /api/v1/tasks

Output:

(Manifest only—matches exactly /api/v1/tasks)

matches:
  - path:
      type: PathPrefix
      value: /api/v1

Output:

(Matches /api/v1, /api/v1/tasks, /api/v1/tasks/123)

matches:
  - path:
      type: RegularExpression
      value: "/api/v[0-9]+/tasks"

Output:

(Matches /api/v1/tasks, /api/v2/tasks, etc.)

Header Matching

Route based on HTTP headers—something Ingress couldn't do without annotations:

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /api
        headers:
          - name: X-API-Version
            value: "2"
    backendRefs:
      - name: task-api-v2
        port: 8000
  - matches:
      - path:
          type: PathPrefix
          value: /api
    backendRefs:
      - name: task-api-v1
        port: 8000

Output:

httproute.gateway.networking.k8s.io/task-api-route configured

Requests with X-API-Version: 2 header go to the v2 backend. All other requests go to v1. No annotations. No vendor lock-in.

Query Parameter Matching

Route based on URL query parameters:

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /api
        queryParams:
          - name: version
            value: "beta"
    backendRefs:
      - name: task-api-beta
        port: 8000

Output:

(Matches /api/tasks?version=beta)

Requests with ?version=beta go to the beta backend.

Checking HTTPRoute Status

kubectl get httproute task-api-route -n task-api -o yaml

Output:

status:
  parents:
    - parentRef:
        name: task-api-gateway
      controllerName: gateway.envoyproxy.io/gatewayclass-controller
      conditions:
        - type: Accepted
          status: "True"
          reason: Accepted
        - type: ResolvedRefs
          status: "True"
          reason: ResolvedRefs

The status shows whether the route was accepted and whether its backend references were resolved.


Role Separation Model

The three-tier model enables clear ownership boundaries:

┌────────────────────────────────────────────────────────────────────────┐
│                         Role Separation                                 │
├────────────────────────────────────────────────────────────────────────┤
│                                                                         │
│   CLUSTER ADMIN                                                         │
│   ┌──────────────────────────────────────────────────────────────────┐ │
│   │ Creates: GatewayClass                                             │ │
│   │ RBAC: cluster-admin or custom role with gatewayclass permissions  │ │
│   │ Responsibility: Which controllers are available                   │ │
│   └──────────────────────────────────────────────────────────────────┘ │
│                              │                                          │
│                              ▼                                          │
│   PLATFORM TEAM                                                         │
│   ┌──────────────────────────────────────────────────────────────────┐ │
│   │ Creates: Gateway                                                  │ │
│   │ RBAC: namespace role with gateway permissions                     │ │
│   │ Responsibility: Entry points, TLS, listener configuration        │ │
│   └──────────────────────────────────────────────────────────────────┘ │
│                              │                                          │
│                              ▼                                          │
│   APPLICATION DEVELOPER                                                 │
│   ┌──────────────────────────────────────────────────────────────────┐ │
│   │ Creates: HTTPRoute, GRPCRoute                                     │ │
│   │ RBAC: namespace role with route permissions                       │ │
│   │ Responsibility: Routing rules for their services                  │ │
│   └──────────────────────────────────────────────────────────────────┘ │
│                                                                         │
└────────────────────────────────────────────────────────────────────────┘

RBAC Example

Platform team RBAC allowing Gateway creation but not GatewayClass:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: gateway-admin
  namespace: gateway-system
rules:
  - apiGroups: ["gateway.networking.k8s.io"]
    resources: ["gateways"]
    verbs: ["create", "delete", "get", "list", "update", "watch"]

Output:

role.rbac.authorization.k8s.io/gateway-admin created

Application developer RBAC allowing HTTPRoute but not Gateway:

apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: route-admin
  namespace: task-api
rules:
  - apiGroups: ["gateway.networking.k8s.io"]
    resources: ["httproutes"]
    verbs: ["create", "delete", "get", "list", "update", "watch"]

Output:

role.rbac.authorization.k8s.io/route-admin created

Developers with route-admin can modify routing for their services but cannot touch the Gateway configuration.

Comparison to Ingress

AspectIngressGateway API
Resource ownershipSingle resource for all configThree tiers with clear ownership
RBAC granularityAll-or-nothing on IngressSeparate permissions per tier
TLS configurationMixed with routing rulesSeparate in Gateway
Who can break TLS?Anyone with Ingress accessOnly platform team with Gateway access
Audit trailUnclear who changed whatClear ownership per resource

Key Features Beyond Ingress

Gateway API includes features that Ingress could only achieve through vendor-specific annotations.

Traffic Splitting

Send a percentage of traffic to different backends:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: canary-route
  namespace: task-api
spec:
  parentRefs:
    - name: task-api-gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /api
      backendRefs:
        - name: task-api-stable
          port: 8000
          weight: 90
        - name: task-api-canary
          port: 8000
          weight: 10

Output:

httproute.gateway.networking.k8s.io/canary-route created

90% of traffic goes to stable, 10% to canary. No annotations. Native in the specification.

Request Mirroring

Copy traffic to another backend for testing without affecting responses:

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /api
    backendRefs:
      - name: task-api
        port: 8000
    filters:
      - type: RequestMirror
        requestMirror:
          backendRef:
            name: task-api-shadow
            port: 8000

Output:

(Manifest only—mirrors all requests to shadow environment)

Production traffic goes to task-api. Copies go to task-api-shadow for testing. Shadow responses are discarded.

URL Rewriting

Modify the URL before forwarding:

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /old-api
    filters:
      - type: URLRewrite
        urlRewrite:
          path:
            type: ReplacePrefixMatch
            replacePrefixMatch: /api/v1
    backendRefs:
      - name: task-api
        port: 8000

Output:

(Requests to /old-api/tasks become /api/v1/tasks)

Request Header Modification

Add or remove headers:

rules:
  - matches:
      - path:
          type: PathPrefix
          value: /api
    filters:
      - type: RequestHeaderModifier
        requestHeaderModifier:
          add:
            - name: X-Forwarded-By
              value: gateway-api
          remove:
            - X-Internal-Header
    backendRefs:
      - name: task-api
        port: 8000

Output:

(Adds X-Forwarded-By header, removes X-Internal-Header)

Feature Comparison Table

FeatureIngressGateway API
Path-based routingYesYes
Host-based routingYesYes
Header-based routingAnnotation-dependentNative
Query parameter routingAnnotation-dependentNative
Traffic splittingAnnotation-dependentNative
Request mirroringAnnotation-dependentNative
URL rewritingAnnotation-dependentNative
Header modificationAnnotation-dependentNative
gRPC supportNoGRPCRoute
TCP/UDP supportNoTCPRoute, UDPRoute
Status reportingMinimalRich conditions

Cross-Namespace Routing with ReferenceGrant

By default, HTTPRoute can only reference Services in the same namespace. To reference Services in other namespaces, you need a ReferenceGrant.

Why This Matters

Imagine a shared database service in the databases namespace. Application teams in task-api namespace want to route to it. Without ReferenceGrant, this fails—the route cannot cross namespace boundaries.

ReferenceGrant allows the databases namespace to grant access:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-task-api-routes
  namespace: databases
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: HTTPRoute
      namespace: task-api
  to:
    - group: ""
      kind: Service

Output:

referencegrant.gateway.networking.k8s.io/allow-task-api-routes created

Now HTTPRoutes in task-api namespace can reference Services in databases namespace.

Security Model

ReferenceGrant implements the "trust on the server side" pattern:

  • The target namespace grants access (databases grants to task-api)
  • The source namespace cannot unilaterally access other namespaces
  • Owners of shared resources control who can reference them

Reference Implementations

Gateway API is a specification. Multiple controllers implement it:

ImplementationMaintainerNotes
Envoy GatewayCNCFPure Gateway API focus, our choice for this chapter
IstioGoogle/CNCFService mesh with Gateway API support
TraefikTraefik LabsSupports both Traefik CRDs and Gateway API
Kong GatewayKongAPI gateway with Gateway API support
ContourVMwareEnvoy-based with Gateway API support
NGINX Gateway FabricF5/NGINXNGINX-based Gateway API

Choosing Envoy Gateway

We use Envoy Gateway for this chapter because:

  1. Pure Gateway API focus—built specifically for Gateway API, not retrofitted
  2. CNCF project—vendor-neutral, community-driven
  3. Envoy-based—same proxy powering Istio, production-proven
  4. Policy CRDs—BackendTrafficPolicy, SecurityPolicy for advanced features
  5. Active development—rapid feature iteration

The next lesson covers Envoy Gateway installation and architecture.


Exercises

Exercise 1: Read the Gateway API Specification

Visit the official Gateway API documentation at gateway-api.sigs.k8s.io.

Find answers to:

  1. What's the current stable version of Gateway API?
  2. Which route types are in the Standard channel (stable)?
  3. Which route types are in the Experimental channel?

Expected findings:

  • Standard: Gateway, GatewayClass, HTTPRoute, ReferenceGrant
  • Experimental: GRPCRoute, TCPRoute, TLSRoute, UDPRoute

Exercise 2: Write GatewayClass YAML

Create a GatewayClass for Envoy Gateway controller:

apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: my-envoy-gateway
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller

Questions:

  • Is GatewayClass namespace-scoped or cluster-scoped?
  • Would you apply this yourself, or let the Helm chart handle it?

Answers:

  • Cluster-scoped (no namespace in metadata)
  • Typically let Helm chart create it during installation

Exercise 3: Write Gateway YAML

Create a Gateway with HTTP listener on port 80:

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: practice-gateway
  namespace: default
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80

Validate syntax (if you have a cluster):

kubectl apply --dry-run=client -f gateway.yaml

Output:

gateway.gateway.networking.k8s.io/practice-gateway created (dry run)

Exercise 4: Write HTTPRoute YAML

Create an HTTPRoute for Task API:

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: task-api-route
  namespace: task-api
spec:
  parentRefs:
    - name: task-api-gateway
      namespace: task-api
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: task-api
          port: 8000

Validate syntax:

kubectl apply --dry-run=client -f httproute.yaml

Output:

httproute.gateway.networking.k8s.io/task-api-route created (dry run)

Exercise 5: Map Resources to Teams

For each resource, identify which team should own it:

ResourceTeamWhy?
GatewayClass eg
Gateway main-gateway
HTTPRoute task-api-route
ReferenceGrant allow-cross-ns

Answers:

  1. GatewayClass: Cluster Admin (infrastructure-level)
  2. Gateway: Platform Team (entry point configuration)
  3. HTTPRoute: Application Developer (routing for their service)
  4. ReferenceGrant: Owner of target namespace (grants access to their resources)

Reflect on Your Skill

You built a traffic-engineer skill in Lesson 0. Based on what you learned about Gateway API:

Add Gateway API Pattern Generation

Your skill should now include templates for:

  1. GatewayClass (rarely needed—check if it exists first)
  2. Gateway with configurable listeners (HTTP, HTTPS)
  3. HTTPRoute with path, header, and query parameter matching
  4. ReferenceGrant for cross-namespace routing

What's the minimum valid configuration your skill should generate?

# Minimum: Gateway + HTTPRoute
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: {{ service }}-gateway
  namespace: {{ namespace }}
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: {{ service }}-route
  namespace: {{ namespace }}
spec:
  parentRefs:
    - name: {{ service }}-gateway
  rules:
    - matches:
        - path:
            type: PathPrefix
            value: /
      backendRefs:
        - name: {{ service }}
          port: {{ port }}

Handle Role Separation

How should your skill handle the role separation model when generating resources?

Decision logic to encode:

ScenarioGenerate
Single developer, local clusterGateway + HTTPRoute (same namespace)
Team environment, shared gatewayHTTPRoute only (attach to existing Gateway)
Platform team setupGateway in gateway-system namespace
Cross-namespace routing neededInclude ReferenceGrant template

Your skill should ask about the deployment context before generating resources.


Try With AI

This lesson marks the transition from manual learning to AI collaboration. Use your traffic-engineer skill to explore Gateway API concepts.

Generate Gateway Resources

Ask your traffic-engineer skill to generate Gateway API configuration:

Using my traffic-engineer skill, generate Gateway API configuration for my Task API:

- Namespace: task-api
- Service name: task-api
- Port: 8000
- Path prefix: /api/

Include both Gateway and HTTPRoute resources.

What you're learning: AI generates complete Gateway API configurations. Review the output—does it include the correct GatewayClass reference? Is the HTTPRoute properly attached to the Gateway via parentRefs?

Evaluate and Refine

Check AI's output against what you learned:

  • Is the GatewayClass eg (Envoy Gateway's default)?
  • Does the HTTPRoute reference the Gateway by name?
  • Is the backend port correct (8000)?

If something is missing or incorrect, provide feedback:

The HTTPRoute needs to specify the namespace for the backend service.
Also, add hostnames to the Gateway listener for production use.

Explore Role Separation

Ask about multi-team scenarios:

I work on a platform team managing shared infrastructure. Generate a
Gateway configuration that:

- Gateway lives in gateway-system namespace
- Application teams deploy HTTPRoutes in their own namespaces
- Include the ReferenceGrant needed for cross-namespace routing

Show me the resources for the platform team and for the task-api team separately.

What you're learning: AI adapts configurations to different organizational models. Verify the ReferenceGrant correctly allows the task-api namespace to reference the Gateway in gateway-system.

Safety Note

When generating Gateway API resources, verify the GatewayClass exists in your cluster before applying. Different Gateway controllers (Envoy Gateway, Istio, Traefik) use different GatewayClass names. Always run kubectl get gatewayclass first to see what's available.


On this page