Skip to main content

Gateway API with Envoy Gateway

In this setup, we will use the AWS Load Balancer Controller to provision a high-performance NLB that terminates TLS via ACM, and then passes the traffic to Envoy pods which handle the smart routing to your apps.

High-Level Architecture


Step 1: Prerequisites

Before we start, ensure your EKS cluster has the following:
  1. AWS Load Balancer Controller (LBC) installed. This is mandatory as it’s the component that actually talks to AWS to create the NLB.
  2. VPC CNI (standard on EKS) for “IP” target mode (faster performance).
  3. An ACM Certificate ARN (e.g., arn:aws:acm:region:account:certificate/ID) for your domain.

Step 2: Install Gateway API CRDs & Envoy Gateway

Gateway API is not installed by default in K8s clusters. You must install the definitions first.
# 1. Install Standard Gateway API CRDs
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.1.0/standard-install.yaml

# 2. Install Envoy Gateway via Helm
helm install eg oci://docker.io/envoyproxy/gateway-helm \
  --version v1.6.3 \
  -n envoy-gateway-system \
  --create-namespace


Step 3: Configure the NLB (EnvoyProxy & GatewayClass)

We need to tell Envoy Gateway how to provision the AWS infrastructure. We do this using a provider-specific configuration.

1. Define the EnvoyProxy (The AWS Settings)

This is where we put our NLB annotations.
apiVersion: gateway.envoyproxy.io/v1alpha1
kind: EnvoyProxy
metadata:
  name: aws-nlb-config
  namespace: envoy-gateway-system
spec:
  provider:
    type: Kubernetes
    kubernetes:
      envoyService:
        annotations:
          service.beta.kubernetes.io/aws-load-balancer-type: "external"
          service.beta.kubernetes.io/aws-load-balancer-nlb-target-type: "ip"
          service.beta.kubernetes.io/aws-load-balancer-scheme: "internet-facing"
          # TLS Termination at NLB level
          service.beta.kubernetes.io/aws-load-balancer-ssl-cert: <YOUR_ACM_ARN>
          service.beta.kubernetes.io/aws-load-balancer-ssl-ports: "443"

2. Define the GatewayClass

This links the Envoy controller to your specific AWS config.
apiVersion: gateway.networking.k8s.io/v1
kind: GatewayClass
metadata:
  name: envoy-gateway-class
spec:
  controllerName: gateway.envoyproxy.io/gatewayclass-controller
  parametersRef:
    group: gateway.envoyproxy.io
    kind: EnvoyProxy
    name: aws-nlb-config
    namespace: envoy-gateway-system


Step 4: Provision the Gateway (The NLB)

Creating this resource triggers the AWS Load Balancer Controller to spin up your NLB.
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: my-gateway
  namespace: default
spec:
  gatewayClassName: envoy-gateway-class
  listeners:
  - name: https
    protocol: HTTP # We use HTTP here because NLB terminates TLS and sends plain HTTP to Envoy
    port: 443
    allowedRoutes:
      namespaces:
        from: All

Note: Check the status with kubectl get gateway. Once the PROGRAMMED status is True, you will see an ADDRESS which is your NLB DNS.

Step 5: Route-Based Routing (HTTPRoute)

Now, let’s point example.com/app1 to Service A and example.com/app2 to Service B.
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
  name: app-routes
  namespace: default
spec:
  parentRefs:
  - name: my-gateway
  hostnames:
  - "example.com"
  rules:
  - matches:
    - path:
        type: PathPrefix
        value: /app1
    backendRefs:
    - name: app1-service
      port: 80
  - matches:
    - path:
        type: PathPrefix
        value: /app2
    backendRefs:
    - name: app2-service
      port: 80


Step 6: Finalize Route 53

  1. Go to the Route 53 Console.
  2. Create an A Record for example.com.
  3. Select Alias to Network Load Balancer.
  4. Choose your region and select the NLB created by the Gateway.

Why this approach in 2026?

  • Role Separation: Your infrastructure team manages the Gateway, while app teams just manage their own HTTPRoute without breaking each other’s configurations.
  • Performance: Envoy is C++ based and extremely lightweight compared to the Lua-heavy NGINX Ingress.
  • Future Proof: Most cloud providers are moving away from Ingress annotations in favor of this standardized API.
Now add advanced filters, like “URL Rewriting” (so /app1 becomes / when it hits the pod), or “Rate Limiting” to this setup?