← Back to DevBytes

Gateway API: Complete Setup and Configuration Guide

What is Gateway API?

Gateway API is a Kubernetes-native API for configuring service networking, developed as a community-driven project under the Kubernetes SIG Network. It represents the next evolution of Kubernetes ingress capabilities, providing a standardized, extensible, and role-oriented way to model service networking across multiple gateway implementations.

Unlike the legacy Ingress API, which was limited to basic HTTP routing with a single monolithic resource, Gateway API introduces a modular resource model that separates infrastructure management from application-level routing. This separation allows cluster operators to manage shared gateway infrastructure while application developers independently define routing rules for their services—all within the same platform.

The API is designed to support a wide range of networking use cases: HTTP and HTTPS routing, TLS termination, traffic splitting for canary deployments, header-based routing, cross-namespace traffic management, and even non-HTTP protocols like gRPC, TCP, and UDP. It is supported by over 30 implementations including Istio, Envoy Gateway, NGINX Gateway Fabric, Traefik, Kong, and many cloud provider load balancer controllers.

Why Gateway API Matters

🚀 Deploy your AI agent in 10 minutes

Managed Hermes hosting. Zero DevOps. 100M tokens/mo included.

Try it free →

For years, the Kubernetes Ingress resource served as the de facto standard for external HTTP access. However, as adoption grew, its limitations became apparent: a single resource crammed annotations to express advanced behavior, no clean separation of roles, and inconsistent support across implementations. Gateway API solves these problems with a specification-driven approach that is both portable and powerful.

Here are the key reasons Gateway API matters for modern Kubernetes deployments:

Core Concepts and Resource Model

Gateway API is built around three primary resource types that form a layered abstraction. Understanding these is essential before diving into configuration.

GatewayClass

A GatewayClass defines a class of gateway infrastructure, similar to how StorageClass defines types of storage. It is cluster-scoped and typically created by the cluster operator or the gateway implementation's controller. Each GatewayClass references a specific controller (like gateway.envoyproxy.io/gateway for Envoy Gateway or istio.io/gateway-controller for Istio). When you create a Gateway, it must reference a GatewayClass that tells Kubernetes which implementation will provision and manage the underlying load balancer or proxy.

Gateway

A Gateway represents a concrete instance of network infrastructure—typically a load balancer, reverse proxy, or edge router. It is namespace-scoped (though it can serve routes from multiple namespaces). A Gateway specifies listeners (ports, protocols, TLS configuration) and can attach one or more Route resources. The Gateway resource is owned and managed by infrastructure administrators.

HTTPRoute and Other Routes

Routes define how traffic arriving at a Gateway listener is forwarded to backend services. The most common route type is HTTPRoute, which handles HTTP and HTTPS traffic. Other route types include TLSRoute (for TLS passthrough), TCPRoute, UDPRoute, and GRPCRoute. Routes are namespace-scoped and can reference backends (Services) in the same namespace or, with proper RBAC and ReferenceGrant configuration, in other namespaces. Routes are typically managed by application developers.

Complete Setup and Configuration Guide

This section walks you through a complete, working setup from scratch. We will use Envoy Gateway as the implementation due to its simplicity and broad adoption, but the Gateway and HTTPRoute resources are identical across any conformant implementation.

Prerequisites

Step 1: Install Gateway API CRDs

Gateway API ships as a set of Custom Resource Definitions (CRDs) that must be installed in your cluster. The standard CRDs are versioned and available from the upstream Gateway API project. Install the latest stable version (v1.1.0 as of this writing):

# Install Gateway API CRDs (standard channel)
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml

Verify the CRDs are installed:

kubectl get crd | grep gateway.networking.k8s.io
# Expected output:
# gatewayclasses.gateway.networking.k8s.io
# gateways.gateway.networking.k8s.io
# httproutes.gateway.networking.k8s.io
# referencegrants.gateway.networking.k8s.io
# ... (and others depending on the channel)

Step 2: Choose and Install a Gateway Implementation

Gateway API is purely a specification—it requires a backing implementation to actually provision infrastructure and handle traffic. For this guide, we'll use Envoy Gateway, which deploys Envoy proxy instances and manages them via a Kubernetes controller. Install Envoy Gateway using its Helm chart or quickstart manifest:

# Install Envoy Gateway controller
helm repo add envoy-gateway https://gateway.envoyproxy.io/
helm repo update
helm install eg envoy-gateway/gateway-helm \
  --namespace envoy-gateway-system \
  --create-namespace \
  --set config.envoyGateway.provider.type=Kubernetes

Alternatively, use the quickstart YAML:

kubectl apply -f https://github.com/envoyproxy/gateway/releases/download/v1.1.0/quickstart.yaml

Wait for the controller deployment to become ready:

kubectl -n envoy-gateway-system wait deployment/envoy-gateway --for condition=Available --timeout=300s

The installation automatically creates a default GatewayClass named eg. Let's confirm:

kubectl get gatewayclass
# NAME      CONTROLLER                         ACCEPTED   AGE
# eg        gateway.envoyproxy.io/gateway      true       30s

Step 3: Deploy Sample Applications

Before creating Gateway and Route resources, we need backend services to route to. Let's deploy two simple applications—a main web app and a canary version—along with their Services:

# Create a namespace for our demo
kubectl create namespace demo-apps

# Deploy the main application (version 1)
cat <

Verify the deployments and services are healthy:

kubectl -n demo-apps get deployments,services
# You should see both deployments available and both services with ClusterIPs

Step 4: Create a Gateway

Now we create a Gateway that will provision a load balancer and listen for HTTP traffic on port 80. The Gateway references the eg GatewayClass installed by Envoy Gateway:

cat <

Let's break down what this does:

  • gatewayClassName: eg tells Envoy Gateway's controller to handle this Gateway
  • listeners defines one HTTP listener on port 80
  • allowedRoutes.namespaces.from: Same means only Routes in the same namespace (demo-apps) can attach to this Gateway. For cross-namespace access, you'd set this to All or use a label selector with Selector

Check the Gateway status—it should show an assigned address once the load balancer is provisioned:

kubectl -n demo-apps get gateway demo-gateway
# NAME            CLASS   ADDRESS          PROGRAMMED   AGE
# demo-gateway    eg      192.168.1.100    True         2m

# The ADDRESS may be a local IP (kind/minikube) or a cloud load balancer hostname
# Save it for testing:
export GATEWAY_IP=$(kubectl -n demo-apps get gateway demo-gateway -o jsonpath='{.status.addresses[0].value}')
echo "Gateway IP: $GATEWAY_IP"

Step 5: Define HTTPRoutes

Routes tell the Gateway how to forward traffic to backend services. Let's create an HTTPRoute that directs all traffic to the v1 application:

cat <

This route does the following:

  • parentRefs attaches the route to the demo-gateway Gateway's http-listener
  • hostnames restricts the route to requests with Host: myapp.example.com. Without this, it would match any host
  • rules.matches.path uses PathPrefix: / to match all paths starting with /
  • backendRefs sends matched traffic to the app-v1-svc Service on port 80

Check the route status:

kubectl -n demo-apps get httproutes
# NAME        HOSTNAMES                 AGE
# app-route   ["myapp.example.com"]     30s

Step 6: Test Your Configuration

With the Gateway provisioned and the Route attached, traffic should flow. Test it using curl with the appropriate Host header:

# Test via the Gateway IP with the correct Host header
curl -H "Host: myapp.example.com" http://$GATEWAY_IP/
# Expected: "Hello from Version 1"

# If you're using kind or minikube and the IP is localhost, use:
curl -H "Host: myapp.example.com" http://localhost:80/
# Or if port-forwarding is needed:
# kubectl -n envoy-gateway-system port-forward svc/envoy-default-eg-e41e7b71 8888:80 &
# curl -H "Host: myapp.example.com" http://localhost:8888/

If you see "Hello from Version 1", your Gateway API configuration is working! The request flowed from the load balancer through Envoy proxy, matched the HTTPRoute rule, and reached the v1 backend service.

Advanced Configuration

Now that the basic setup works, let's explore more sophisticated routing patterns that Gateway API excels at.

TLS Termination

To serve HTTPS traffic, you need a TLS certificate and key stored in a Kubernetes Secret. The Gateway listener references this secret for TLS termination. First, create a self-signed certificate (for testing) or use cert-manager for production:

# Generate a self-signed certificate for testing
openssl req -x509 -nodes -days 365 -newkey rsa:2048 \
  -keyout tls.key -out tls.crt \
  -subj "/CN=myapp.example.com/O=Demo"

# Store it as a Kubernetes Secret in the same namespace as the Gateway
kubectl -n demo-apps create secret tls myapp-tls-secret \
  --cert=tls.crt \
  --key=tls.key

Now update the Gateway to add an HTTPS listener alongside the existing HTTP listener:

cat <

The existing HTTPRoute will work for both listeners (it's bound to the Gateway, not a specific listener, unless you use sectionName). To restrict a route to HTTPS only, set parentRefs.sectionName: https-listener.

Test HTTPS access:

curl -k -H "Host: myapp.example.com" https://$GATEWAY_IP/
# Expected: "Hello from Version 1"
# The -k flag ignores self-signed certificate warnings

Traffic Splitting (Canary Deployments)

One of Gateway API's most powerful features is weighted traffic splitting, enabling canary deployments without additional tooling. Let's modify our HTTPRoute to send 90% of traffic to v1 and 10% to v2:

cat <

Now run several requests to see the distribution in action:

for i in {1..20}; do
  curl -s -H "Host: myapp.example.com" http://$GATEWAY_IP/
done
# You should see approximately 18 "Hello from Version 1" and 2 "Hello from Version 2 (Canary)"

Header-Based Routing

Gateway API supports matching on HTTP headers, allowing you to route requests based on arbitrary header values. This is perfect for A/B testing, feature flags, or routing based on authentication tokens:

cat <

Test the header-based routing:

# Without the header, goes to v1
curl -H "Host: myapp.example.com" http://$GATEWAY_IP/
# Expected: "Hello from Version 1"

# With the X-Canary header, goes to v2
curl -H "Host: myapp.example.com" -H "X-Canary: true" http://$GATEWAY_IP/
# Expected: "Hello from Version 2 (Canary)"

Cross-Namespace Routing with ReferenceGrant

By default, a Gateway only accepts Routes from its own namespace when allowedRoutes.namespaces.from is Same. To allow Routes from other namespaces while maintaining security, Gateway API uses ReferenceGrant resources. A ReferenceGrant explicitly authorizes cross-namespace references between specific resources.

First, create a new namespace and deploy a service there:

kubectl create namespace external-team

cat <

Update the Gateway to allow routes from all namespaces:

cat <

Now create a ReferenceGrant in the backend's namespace that permits HTTPRoutes from demo-apps to reference Services in external-team:

cat <

Create an HTTPRoute in demo-apps that references the service in external-team:

cat <

Test the cross-namespace route:

curl -H "Host: team.example.com" http://$GATEWAY_IP/
# Expected: "Hello from External Team"

Best Practices

As you adopt Gateway API in production, the following practices will help you build secure, scalable, and maintainable ingress architectures:

  • Use separate namespaces for infrastructure and applications. Place Gateways in an infrastructure namespace managed by platform engineers, and keep Routes in application namespaces owned by development teams. This clean separation aligns with the role-based design of Gateway API.
  • Restrict allowedRoutes carefully. Start with from: Same and only expand to All or Selector when cross-namespace routing is explicitly needed. For cross-namespace access, always pair it with a scoped ReferenceGrant—never grant blanket access.
  • Leverage sectionName for listener-specific routes. When a Gateway has multiple listeners (HTTP and HTTPS), use parentRefs.sectionName to bind routes to specific listeners. This prevents HTTP routes from accidentally being exposed on HTTPS listeners and vice versa.
  • Prefer PathPrefix over Exact or Path for most use cases. PathPrefix matches both the exact path and any subpaths, which is usually what you want for web applications. Use Exact only for API endpoints that must not have trailing slashes.
  • Use weighted backends for gradual rollouts. Instead of building custom canary infrastructure, use Gateway API's native weight field on backendRefs. You can adjust weights progressively (5% → 25% → 50% → 100%) by updating the HTTPRoute manifest and reapplying it.
  • Validate Gateway and Route status fields. Always check status.conditions on Gateways and Routes after applying changes. A Programmed: True condition on a Gateway and Accepted: True on a Route confirms the configuration is valid and has been accepted by the implementation.
  • Plan for multiple Gateways. Rather than cramming all routes onto a single Gateway, consider creating separate Gateways for internal and external traffic, or for different compliance zones. Each Gateway can have distinct listeners, TLS configurations, and access controls.
  • Monitor the Gateway implementation's logs. When routes don't behave as expected, the Gateway controller's logs (e.g., the envoy-gateway deployment) often contain detailed information about rejected configurations, missing secrets, or invalid backend references.
  • Keep CRDs up to date. Gateway API is evolving rapidly. Subscribe to the project's release notes and update your CRDs regularly. The API is versioned and follows semantic versioning, so upgrading within a major version is safe.
  • Test with multiple implementations during development. The beauty of Gateway API is portability. Validate your routing configuration against at least two implementations (e.g., Envoy Gateway and Istio) in staging to ensure you're not relying on implementation-specific behavior.

Gateway API represents a significant leap forward for Kubernetes networking, transforming what was once a fragmented landscape of annotation-driven Ingress resources into a cohesive, standardized, and role-oriented API. By adopting its layered resource model—GatewayClasses for infrastructure provisioning, Gateways for shared ingress points, and Routes for application-level traffic rules—teams can achieve a clean separation of concerns that scales with organizational complexity. The configuration walkthrough above takes you from zero to a fully functional gateway with TLS termination, traffic splitting, header-based routing, and cross-namespace routing, all using portable resources that work across any conformant implementation. As the API continues to mature with new route types like GRPCRoute and advanced features like session affinity and rate limiting, investing in Gateway API today positions your infrastructure for the next decade of cloud-native networking.

🚀 Need a reliable AI agent for your project?

Deploy Hermes Agent in 10 minutes. Managed hosting, zero DevOps.

Get Started — $23.99/mo
← Back to all articles