Sitemap

Istio Ambient Mesh on EKS: A Complete Hands-On Guide

12 min readSep 26, 2026

--

Press enter or click to view image in full size
Photo by Chandler Cruttenden on Unsplash

What is ambient mesh ?

Istio ambient mesh gives you mTLS, workload identity, and L4 authorization. In ambient mode we run a per node L4 proxy (ztunnel). ztunnel is responsible for securing connection on specific nodes.

More about side car vs ambient

What are going to do ?

In this post we are configuring ambient mesh on Amazon EKS declaratively using kustomize.

  • An EKS Cluster created using eksctl
  • Gateway API CRDs, and Istio CRDs installation
  • Install istio components (istiod, ztunnel)
  • Google’s public online boutique demo installation
  • An istio ingress gateway behind an AWS NLB
  • Strict mTLS and L4 authorization policies enforced by ztunnel
  • Waypoint proxy (L7 authorization) is out of scope for this article.

Following version are used in demonstration

All the manifest used are in repo

How ambient mode works ?

Ambient mode moves data plane out of your pods and onto each nodes. A daemon set component called ztunnel is deployed along with other components, which handles the transparent traffic interception and mTLS encryption.

Press enter or click to view image in full size
Ambient mesh with ztunnel (L4)

Traffic between meshed pods travel through ztunnel, over an mTLS tunnel called HBONE (HTTP Connection over HTTP/2) on port 15008. ztunnel checks the caller’s identity against the policy before the connection ever reaches the destination workload.

Following components are configured while deploying ambient mesh

  • CRDs — istio-base, and Gateway API CRDs.
  • istiod — Runs as a deployment, and is responsible for configuration, certificates (SPIFFE Indentities), and xDS.
  • istio-cni — Runs as a DaemonSet, and is responsible for detecting pods joining mesh and setting up traffic redirection inside the pods’s network namespace.
  • ztunnel — Runs as a DaemonSet, and is responsble for mTLS, L4 authorization, and L4 telemetry.

Step 1: Creating the EKS Cluster

A three node managed node group cluster is created using eksctl . We will be using a default Amazon VPC CNI.

apiVersion: eksctl.io/v1alpha5
kind: ClusterConfig

metadata:
name: ambient-demo
region: eu-west-1
version: "1.36"
tags:
project: ambient-demo

# 3 AZs -> 3 public + 3 private subnets
availabilityZones: [eu-west-1a, eu-west-1b, eu-west-1c]

vpc:
cidr: 10.20.0.0/16
nat:
gateway: Single # Single | HighlyAvailable
clusterEndpoints:
publicAccess: true # Only do this for demo. Actual production should block public access.
privateAccess: true # nodes talk to the API server inside the VPC
publicAccessCIDRs: # ONLY these IPs can reach the public API endpoint
- "27.34.65.32/32"
# - "REPLACE_WITH_IP_2/32"

iam:
withOIDC: true

accessConfig:
authenticationMode: API
bootstrapClusterCreatorAdminPermissions: true

addons:
- name: vpc-cni
version: latest
configurationValues: |-
env:
ENABLE_PREFIX_DELEGATION: "true"
WARM_PREFIX_TARGET: "1"
POD_SECURITY_GROUP_ENFORCING_MODE: "standard"
- name: coredns
version: latest
- name: kube-proxy
version: latest
- name: eks-pod-identity-agent
version: latest

managedNodeGroups:
- name: ng-default
instanceType: t3.medium
amiFamily: Bottlerocket
desiredCapacity: 3
minSize: 3
maxSize: 5
volumeSize: 40 # Bottlerocket data volume
privateNetworking: true # nodes in private subnets only
labels:
role: general
bottlerocket:
settings:
kubernetes:
max-pods: 110 # pairs with prefix delegation
Press enter or click to view image in full size
Creating Cluster

We can create cluster using following command

eksctl create cluster -f cluster/cluster.yaml

Prefix delegation on, with max-pods: 110, and POD_SECURITY_GROUP_ENFORCING_MODE: standard for Istio ambient.

This is set because when we set this to strict mode, kubelet health probes fail because istio SNATs them from link-local address.

More about this issue

Press enter or click to view image in full size
Conforming POD_SECURITY_GROUP_ENFORCING_MODE
kubectl get ds aws-node -n kube-system \
-o jsonpath='{.spec.template.spec.containers[?(@.name=="aws-node")].env[?(@.name=="POD_SECURITY_GROUP_ENFORCING_MODE")].value}'; echo

Step 2: Install the CRDs

In this step we will be installing CRDs

  • Kubernetes Gateway API
  • Istio CRDs

00-crds/gateway-api/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.2/standard-install.yaml

00-crds/istio-base/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
name: istio-system

00-crds/istio-base/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
helmCharts:
- name: base
repo: https://blob.istio.io/istio-release/charts
version: 1.31.1
releaseName: istio-base
namespace: istio-system
kubeVersion: "1.36.0"
includeCRDs: true
valuesInline:
defaultRevision: default
base:
# Required when applying rendered charts with server-side apply,
# otherwise the chart and istiod fight over failurePolicy.
validationFailurePolicy: Fail

Apply

kustomize build 00-crds/gateway-api | kubectl apply --server-side -f -
kustomize build --enable-helm 00-crds/istio-base | kubectl apply --server-side -f -
Press enter or click to view image in full size
Gateway API CRDs installation
Press enter or click to view image in full size
Istio Base CRDs installation and Namespace istio-system

Step 3: Install istiod, istio-cni, and ztunnel

We will be deploying all three charts in one kustomization, each with its own values files. The profile: ambient values is the key here.

10-istio/values-istiod.yaml

profile: ambient
global:
hub: docker.io/istio
defaultPodDisruptionBudget:
enabled: false # single replica — avoid PDB blocking EKS node drains/AZ rebalancing
base:
validationFailurePolicy: Fail
meshConfig:
accessLogFile: /dev/stdout
pilot:
autoscaleEnabled: false # single demo replica by design — no HA/autoscaling
replicaCount: 1
resources:
requests:
cpu: 200m
memory: 512Mi
limits: # chart default request alone is 500m/2Gi; cap since we've downsized
cpu: 1000m
memory: 1Gi

10-istio/values-cni.yaml

profile: ambient
global:
hub: docker.io/istio

ambient:
dnsCapture: true # already the chart default once ambient is on

resources:
requests:
cpu: 100m
memory: 100Mi
limits: # chart ships requests only — bound worst case on t3.medium nodes
cpu: 200m
memory: 200Mi

terminationGracePeriodSeconds: 60 # headroom for CNI cleanup during rollouts (upstream chart comment)

10-istio/values-ztunnel.yaml

profile: ambient
global:
hub: docker.io/istio

resources:
requests:
cpu: 200m
memory: 512Mi
limits: # ztunnel owns all L4 data-plane traffic per node — cap explicitly
cpu: 500m
memory: 1Gi

logLevel: info

10-istio/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
helmCharts:
- name: istiod
repo: https://blob.istio.io/istio-release/charts
version: 1.31.1
releaseName: istiod
namespace: istio-system
kubeVersion: "1.36.0"
valuesFile: values-istiod.yaml

- name: cni
repo: https://blob.istio.io/istio-release/charts
version: 1.31.1
releaseName: istio-cni
namespace: istio-system
kubeVersion: "1.36.0"
valuesFile: values-cni.yaml

- name: ztunnel
repo: https://blob.istio.io/istio-release/charts
version: 1.31.1
releaseName: ztunnel
namespace: istio-system
kubeVersion: "1.36.0"
valuesFile: values-ztunnel.yaml

Apply Manifest

kustomize build --enable-helm 10-istio | kubectl apply --server-side -f -
Press enter or click to view image in full size
istio-system pods

Step 4: Deploy Online Boutique

Online Boutique is a cloud-first microservices demo application. The application is a web-based e-commerce app where users can browse items, add them to the cart, and purchase them.

Every service runs under its own Kubernetes ServiceAccount, which gives each one a distinct SPIFFE identity in the mesh. We rely on that in the policy steps.

30-online-boutique/namespace.yaml

Get Binaya Sharma’s stories in your inbox

Join Medium for free to get updates from this writer.

This namespace is not yet part of the mesh. We will be adding it later steps.

apiVersion: v1
kind: Namespace
metadata:
name: app

30-online-boutique/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
namespace: app
resources:
- namespace.yaml
- https://github.com/GoogleCloudPlatform/microservices-demo/kustomize/base?ref=v0.10.7
patches:
# The base creates a public LoadBalancer for the frontend.
# We expose it through the Istio ingress gateway instead.
- patch: |-
$patch: delete
apiVersion: v1
kind: Service
metadata:
name: frontend-external

Apply Manifest

kustomize build 30-online-boutique | kubectl apply --server-side -f -
Press enter or click to view image in full size
Pods up and running in app namespace
Press enter or click to view image in full size
Conformation using port-forwarding

Step 5: Add namespace to the mesh

One label enrolls every pod in the namespace. istio-cni sees the label, sets up redirection inside each running pod’s network namespace, and hands the pod to its ztunnel. No restarts is required.

We would need to update 30-online-boutique/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
name: app
labels:
istio.io/dataplane-mode: ambient

Apply manifest

kustomize build 30-online-boutique | kubectl apply --server-side -f -
Namespace labels for ambient mode

Verify mTLS

We can verify mTLS using using the following command

istioctl ztunnel-config workloads --workload-namespace app
Press enter or click to view image in full size
Conforming ztunnel mTLS in app namespace
  • PROTOCOL: HBONE — It means ztunnel is intercepting each pod’s traffic and tunneling it over HBONE (HTTP-based mTLS overlay tunnel) to the
    destination’s ztunnel.
  • WAYPOINT: None — this just means no L7 waypoint proxy is attached to these workloads. In ambient mode, ztunnel alone gives you L4: mTLS, identity, and basic telemetry between pods. A waypoint is an optional, separate proxy you deploy only when you need L7 features (HTTP routing, retries, AuthorizationPolicy with HTTP-level conditions, etc.).

In online-boutique there is already a load-generator deployment. We can inspect the traffic, and see the actual SPIFEE identities

$ kubectl logs -n istio-system -l app=ztunnel -f --prefix --max-log-requests=5

[pod/ztunnel-drqkh/istio-proxy] 2026-09-26T12:00:01.395988Z info access connection complete src.addr=10.20.127.85:42032 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="outbound" bytes_sent=72 bytes_recv=10752 duration="63ms"
[pod/ztunnel-7p2k4/istio-proxy] 2026-09-26T12:00:01.395392Z info access connection complete src.addr=10.20.127.85:49960 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="inbound" bytes_sent=10752 bytes_recv=72 duration="38ms"
[pod/ztunnel-7p2k4/istio-proxy] 2026-09-26T12:00:02.432301Z info access connection complete src.addr=10.20.127.85:49960 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="inbound" bytes_sent=10752 bytes_recv=72 duration="26ms"
[pod/ztunnel-drqkh/istio-proxy] 2026-09-26T12:00:02.432758Z info access connection complete src.addr=10.20.127.85:42034 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="outbound" bytes_sent=72 bytes_recv=10752 duration="27ms"
[pod/ztunnel-drqkh/istio-proxy] 2026-09-26T12:00:03.489273Z info access connection complete src.addr=10.20.127.85:42050 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="outbound" bytes_sent=72 bytes_recv=10752 duration="46ms"
[pod/ztunnel-7p2k4/istio-proxy] 2026-09-26T12:00:03.488812Z info access connection complete src.addr=10.20.127.85:49960 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="inbound" bytes_sent=10752 bytes_recv=72 duration="45ms"
[pod/ztunnel-7p2k4/istio-proxy] 2026-09-26T12:00:04.522673Z info access connection complete src.addr=10.20.127.85:49960 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="inbound" bytes_sent=10752 bytes_recv=72 duration="26ms"
[pod/ztunnel-drqkh/istio-proxy] 2026-09-26T12:00:04.523132Z info access connection complete src.addr=10.20.127.85:42066 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="outbound" bytes_sent=72 bytes_recv=10752 duration="26ms"
[pod/ztunnel-7p2k4/istio-proxy] 2026-09-26T12:00:05.551088Z info access connection complete src.addr=10.20.127.85:49960 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="inbound" bytes_sent=10752 bytes_recv=72 duration="19ms"
[pod/ztunnel-drqkh/istio-proxy] 2026-09-26T12:00:05.551546Z info access connection complete src.addr=10.20.127.85:49344 src.workload="curl-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.21:15008 dst.hbone_addr=10.20.181.21:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-58b4dc4d4d-r69c6" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/frontend" dst.cluster="Kubernetes" direction="outbound" bytes_sent=72 bytes_recv=10752 duration="20ms"

src.identity / dst.identity pair is the proof that the connection was mutually authenticated with workload certificates issued by istiod.

Step 6: Expose the Frontend through an Ingress Gateway

Now we will be using a NLB to expose the frontend. We will be using API Gateway, and thus we will create a HTTP Route as well.

20-ingress/namespace.yaml

apiVersion: v1
kind: Namespace
metadata:
name: istio-ingress

20-ingress/gateway.yaml

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: public-gateway
namespace: istio-ingress
spec:
gatewayClassName: istio
infrastructure:
annotations:
service.beta.kubernetes.io/aws-load-balancer-type: "nlb"
listeners:
- name: http
protocol: HTTP
port: 80
allowedRoutes:
namespaces:
from: Selector
selector:
matchLabels:
kubernetes.io/metadata.name: app

20-ingress/httproute.yaml

apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: frontend
namespace: app
spec:
parentRefs:
- name: public-gateway
namespace: istio-ingress
rules:
- backendRefs:
- name: frontend
port: 80

20-ingress/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- namespace.yaml
- gateway.yaml

Apply

kustomize build 20-ingress | kubectl apply --server-side -f -
Press enter or click to view image in full size
Press enter or click to view image in full size
Publicly exposed using NLB

In side it show local . I wanted to change it to AWS. It can be easily done by editing 30-online-boutique/kustomization.yaml

Add the following patch to the patches section

  # Show "AWS" in the frontend's platform badge instead of "local".
- patch: |-
apiVersion: apps/v1
kind: Deployment
metadata:
name: frontend
spec:
template:
spec:
containers:
- name: server
env:
- name: ENV_PLATFORM
value: "aws"
kustomize build 30-online-boutique | kubectl apply --server-side -f -

Now it is updated to

Press enter or click to view image in full size
Updated ENV_PLATFORM

Step 7: Securing with L4 PeerAuthentication, and Authorization policy (ztunnel)

We already have mTLS configured in cluster. Now we want to have a authorization and authentication policies that depends on L4. We will be using L4 attributes: source principal, namespaces, IP, Destination Port will are available to ztunnel.

  1. Strict mTLS, so plaintext from outside the mesh is refused.
  2. A namespace wide allow list: only callers in app namespace or the ingress gateway
  3. A database rule that denty every caller of redis-cart except cartservice

40-policies/authz-l4.yaml

apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: strict-mtls
namespace: app
spec:
mtls:
mode: STRICT
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-namespace-and-ingress
namespace: app
spec:
action: ALLOW
rules:
- from:
- source:
namespaces: ["app"]
- source:
principals:
- cluster.local/ns/istio-ingress/sa/public-gateway-istio
---
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: redis-only-from-cartservice
namespace: app
spec:
selector:
matchLabels:
app: redis-cart
action: DENY
rules:
- from:
- source:
notPrincipals:
- cluster.local/ns/app/sa/cartservice

Before applying these policies, let was create a test redis pod in app namespace. This pod should be able to be PING and get back PONG from the application’s redis pod.

kubectl -n app run redis-test --image=redis:7-alpine  --restart=Never -- sleep 3600
kubectl -n app wait --for=condition=Ready pod/redis-test
kubectl -n app exec redis-test -- redis-cli -h redis-cart ping
Press enter or click to view image in full size
Working PING PONG

Now let’s apply the policies

kustomize build 40-policies | kubectl apply --server-side -f -

After applying the policy PING failed, because we only allowed redis to be called by cartservice

Press enter or click to view image in full size
Failed PING PONG after policy application

We can also see failure logs in ztunnel

Press enter or click to view image in full size
Failed logs
❯ kubectl -n istio-system logs ds/ztunnel --tail=50 | grep -i denied
Found 3 pods, using pod/ztunnel-7p2k4
2026-09-26T12:35:08.970618Z error access connection complete src.addr=10.20.181.26:44112 src.workload="redis-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.23:15008 dst.hbone_addr=10.20.181.23:6379 dst.service="redis-cart.app.svc.cluster.local" dst.workload="redis-cart-788fbddc7-76pmt" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/default" dst.cluster="Kubernetes" direction="inbound" bytes_sent=0 bytes_recv=0 duration="0ms" error="connection closed due to policy rejection: explicitly denied by: app/redis-only-from-cartservice"

We also had another policy to block anything from other namespace (apart from istio-ingress namespace). Lets test that

Press enter or click to view image in full size
Unable to curl from default namespace
❯ kubectl -n istio-system logs ds/ztunnel --tail=50 | grep -i denied
Found 3 pods, using pod/ztunnel-7p2k4
2026-09-26T12:35:08.970618Z error access connection complete src.addr=10.20.181.26:44112 src.workload="redis-test" src.namespace="app" src.identity="spiffe://cluster.local/ns/app/sa/default" src.cluster="Kubernetes" dst.addr=10.20.181.23:15008 dst.hbone_addr=10.20.181.23:6379 dst.service="redis-cart.app.svc.cluster.local" dst.workload="redis-cart-788fbddc7-76pmt" dst.namespace="app" dst.identity="spiffe://cluster.local/ns/app/sa/default" dst.cluster="Kubernetes" direction="inbound" bytes_sent=0 bytes_recv=0 duration="0ms" error="connection closed due to policy rejection: explicitly denied by: app/redis-only-from-cartservice"
2026-09-26T12:40:03.337349Z error access connection complete src.addr=10.20.127.86:47312 dst.addr=10.20.181.25:8080 dst.service="frontend.app.svc.cluster.local" dst.workload="frontend-697646cccf-hvxfm" dst.namespace="app" dst.cluster="Kubernetes" direction="inbound" bytes_sent=0 bytes_recv=0 duration="0ms" error="connection closed due to policy rejection: explicitly denied by: istio-system/istio_converted_static_strict"

Step 8: Visualize in Kiali

ztunnel exports L4 metrics, and we can use kiali to simply visualize mesh configuration.

50-addons/kustomization.yaml

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- https://raw.githubusercontent.com/istio/istio/release-1.31/samples/addons/prometheus.yaml
- https://raw.githubusercontent.com/istio/istio/release-1.31/samples/addons/kiali.yaml

Apply

kustomize build 50-addons | kubectl apply --server-side -f -
kubectl -n istio-system rollout status deploy/kiali
istioctl dashboard kiali

Although we have also installed prometheus, but there is no persistent storage. Please used you monitoring stack or managed solutions.

Press enter or click to view image in full size
mTLS enabled traffice and all services

Clean up

Delete in reverse order. Remove the Gateway before the cluster so the NLB is released and doesn’t linger in your account:

kubectl -n app delete pod redis-test --ignore-not-found
kustomize build 50-addons | kubectl delete --ignore-not-found -f -
kustomize build 40-policies | kubectl delete --ignore-not-found -f -
kustomize build 30-online-boutique | kubectl delete --ignore-not-found -f -
kustomize build 20-ingress | kubectl delete --ignore-not-found -f -
kustomize build --enable-helm 10-istio | kubectl delete --ignore-not-found -f -
kustomize build --enable-helm 00-crds/istio-base | kubectl delete --ignore-not-found -f -
kustomize build 00-crds/gateway-api | kubectl delete --ignore-not-found -f -

eksctl delete cluster -f cluster/cluster.yaml

Wrapping up and Future

We went from an empty EKS cluster to a ambient mesh with strict mTLS everywhere and an L4 database lockdown enforced by ztunnel, all as plain Kustomize directories you can commit and hand to Argo CD or Flux.

When you need HTTP-aware policy, retries or traffic splitting, ambient’s optional waypoint proxies add that L7 layer on top of what we have.

May the Force be with you!

--

--

Binaya Sharma
Binaya Sharma

Written by Binaya Sharma

Hi, I'm Binaya, a passionate DevOps engineer with a focus on DevOps space. I'm constantly exploring new tools.