
Series of blog posts show progress of updating/adding to EKS Cluster, this post covers adding Gateway API. See part 2 below:
Gateway API:
- Kubernetes positions Gateway API as the next generation of service networking for L4/L7 routing.
Flow & Steps Forward:
- We’ll use the stable Envoy Gateway
v1.9.2Helm chart.- Its chart supplies compatible Gateway API and Envoy CRDs.
- No ALB/NLB hourly charges.
- No new Terraform, IAM, or Pod Identity initially.
- Managed completely through Argo CD.
- Teaches the portable Gateway API objects.
- Test locally through
kubectl port-forward. - Later, repeat the exercise with AWS Load Balancer Controller to create real ALBs/NLBs.
Implementation Phases:
- Start with Envoy Gateway using an internal
ClusterIP: - Install Envoy Gateway and Gateway API CRDs.
- Update the lab-platform and workload AppProjects.
- Deploy a small ARM64-compatible demonstration application.
- Add GatewayClass, Gateway, and HTTPRoute.
- Port-forward Envoy and test routing with curl.
- Test path matching, redirects, header matching, and traffic splitting.
- Optionally add AWS Load Balancer Controller for public ALB/NLB routing.
| Phase | Purpose | Resources |
|---|---|---|
| Phase 1 | Install the controller and CRDs | Envoy Gateway Helm chart |
| Phase 2A | Prepare platform ownership | AppProject, Application, Namespace |
| Phase 2B | Select and configure Envoy | GatewayClass, EnvoyProxy |
| Phase 2C | Create the listener | Gateway |
| Phase 3A | Deploy the test application | Deployment, Service |
| Phase 3B | Connect traffic to the application | HTTPRoute |
| Phase 4 | Test routing | Port-forward, curl, status checks |
| Phase 5 | Advanced routing | Headers, paths, traffic splitting, TLS |
Planned Gitops Structure:
applications/lab/├── platform/│ ├── envoy-gateway.yaml│ └── envoy-gateway-config.yaml└── workloads/ └── gateway-demo.yamlapps/├── envoy-gateway/│ └── values.yaml├── envoy-gateway-config/│ ├── namespace.yaml│ ├── gatewayclass.yaml│ ├── envoy-proxy.yaml│ └── gateway.yaml└── gateway-demo/ ├── deployment.yaml ├── service.yaml └── httproute.yaml
| Resource | Responsibility |
|---|---|
GatewayClass | Selects Envoy Gateway as the implementation |
EnvoyProxy | Forces the generated Envoy service to ClusterIP |
Gateway | Defines the HTTP listener |
HTTPRoute | Routes requests to the application |
Service | Provides the route backend |
Deployment | Runs the demonstration application |
Phase 1: Install the controller and CRDs & Envoy Gateway Helm Chart
apps/envoy-gateway/values.yaml
- Sources
- ttps://gateway.envoyproxy.io/docs/install/install-argocd/
- https://gateway.envoyproxy.io/docs/install/gateway-helm-api/
- This configures the Envoy Gateway control plane.
- The
service.typehere applies to the controller service—not yet to the Envoy data-plane proxy we will create later. - Envoy Gateway’s chart installs compatible Gateway API and Envoy Gateway CRDs by default.
applications/lab/platform/envoy-gateway.yaml
- Sources
ServerSideApply=trueis important because Gateway API CRDs can exceed the normal annotation-size limit.- This matches Envoy Gateway’s official Argo CD guidance & keeps CRD ownership within the same Argo CD Application, the separate CRD chart supports choosing
standardinstead of the default experimental channel.
===========metadata: annotations: argocd.argoproj.io/sync-wave: "-2"spec: sources: # Install only the standard Gateway API CRDs and the # Envoy Gateway extension CRDs. - repoURL: docker.io/envoyproxy chart: gateway-crds-helm targetRevision: v1.6.7 helm: releaseName: envoy-gateway-crds valuesObject: crds: gatewayAPI: enabled: true channel: standard envoyGateway: enabled: true ===========
lab-platform.yaml:
- Update the file above w/the following code below
===========spec: sourceRepos: - docker.io/envoyproxy=========== destinations: - namespace: envoy-gateway-system server: https://kubernetes.default.svc=========== clusterResourceWhitelist: - group: admissionregistration.k8s.io kind: ValidatingAdmissionPolicy - group: admissionregistration.k8s.io kind: ValidatingAdmissionPolicyBinding - group: admissionregistration.k8s.io kind: ValidatingWebhookConfiguration - group: admissionregistration.k8s.io kind: MutatingWebhookConfiguration - group: gateway.networking.k8s.io kind: GatewayClass - group: karpenter.k8s.aws kind: EC2NodeClass - group: karpenter.sh kind: NodePool=========== namespaceResourceWhitelist: - group: "" kind: ConfigMap - group: "" kind: Secret - group: batch kind: Job
Phase 2a: Prepare platform ownership & AppProject, Application, Namespace
lab-platform.yaml:
- Update the file above w/the following code below
=========== - namespace: gateway-demo server: https://kubernetes.default.svc===========
applications/lab/platform/envoy-gateway-config.yaml
- Sources:
- For now, it only owns the namespace. This Application will eventually own:
GatewayClassEnvoyProxyGateway
apps/envoy-gateway-config/namespace.yaml
- We explicitly track the namespace in Git instead of relying only on
CreateNamespace=truebecause this lets us manage its labels declaratively.
===========apiVersion: v1kind: Namespacemetadata: name: gateway-demo labels: app.kubernetes.io/name: gateway-demo app.kubernetes.io/part-of: gateway-api-lab app.kubernetes.io/managed-by: argocd environment: lab # Report restricted Pod Security Standard violations. # These are warnings rather than deployment-blocking enforcement. pod-security.kubernetes.io/audit: restricted pod-security.kubernetes.io/warn: restricted===========
Phase 2b: Select and configure Envoy for GatewayClass, EnvoyProxy
- Nothing will receive traffic yet, and no AWS load balancer should be created. Phase 2B adds only two resources:
GatewayClass— selects Envoy Gateway as the controller.EnvoyProxy— defines how the future Envoy data plane should run.
lab-platform.yaml:
=========== # GatewayClass is cluster-scoped and selects the controller. - group: gateway.networking.k8s.io kind: GatewayClass=========== # EnvoyProxy is an Envoy Gateway configuration resource. - group: gateway.envoyproxy.io kind: EnvoyProxy===========
apps/envoy-gateway-config/gatewayclass.yaml
GatewayClassis similar to how aStorageClassselects a storage implementation.- It describes the type of Gateway infrastructure available to the cluster.
===========apiVersion: gateway.networking.k8s.io/v1kind: GatewayClassmetadata: name: envoy annotations: argocd.argoproj.io/sync-wave: "-1"===========
- Tells Kubernetes that Envoy Gateway is responsible for reconciling the resource.
===========spec: controllerName: gateway.envoyproxy.io/gatewayclass-controller===========
apps/envoy-gateway-config/envoy-proxy.yaml
- Sources:
- Envoy Gateway normally creates a
LoadBalancerService for the data-plane proxy. - Specifying
ClusterIPkeeps it internal and prevents AWS from provisioning a billable load balancer.
| Component | Purpose | Namespace |
|---|---|---|
| Envoy Gateway controller | Watches Gateway API objects | envoy-gateway-system |
| Envoy proxy data plane | Eventually receives application traffic | Generated after Phase 2C |
- At this point,
EnvoyProxyis only configuration. Because noGatewayreferences it yet, Envoy Gateway should not create the proxy Deployment or Service.
Phase 2c: Create the listener for Gateway
- This phase adds one file and one AppProject permission. The Gateway will reference the
EnvoyProxyfrom Phase 2B, causing Envoy Gateway to generate:- One internal Envoy Deployment.
- One internal
ClusterIPService. - One HTTP listener on port 80.
- There is still no application or route behind it yet.
lab-platform.yaml:
=========== # Gateway defines the platform-owned network listener. - group: gateway.networking.k8s.io kind: Gateway===========
apps/envoy-gateway-config/gateway.yaml
- Sources:
- This resource means:
===========gatewayClassName: envoy===========
- Selects the envoy gateway controller
===========infrastructure: parametersRef:===========
- Links this Gateway to the
gateway-demo-proxyconfiguration, which specifiesClusterIP. - Creates an HTTP listener with:
- Port:
80 - Hostname:
gateway-demo.local - Allowed route type:
HTTPRoute - Allowed namespace: only
gateway-demo
- Port:
===========listeners:===========
- The
Samerestriction prevents routes in unrelated namespaces from attaching themselves to this Gateway. Gateway API supportsSame,All, or label-selected namespaces;Sameis the narrowest starting point.