{"id":355,"date":"2026-01-08T05:15:55","date_gmt":"2026-01-08T05:15:55","guid":{"rendered":"https:\/\/blog.ngocha.biz\/?p=355"},"modified":"2026-01-08T05:15:55","modified_gmt":"2026-01-08T05:15:55","slug":"setup-istio-ambient-mode","status":"publish","type":"post","link":"https:\/\/blog.ngocha.biz\/?p=355","title":{"rendered":"How to Set Up Istio in Ambient Mode (Detailed Guide)"},"content":{"rendered":"<p>In this blog, you will learn how to set up Istio Ambient Mode and validate Layer 4 (ztunnel) and Layer 7 (waypoint proxy) traffic on a <a href=\"https:\/\/devopscube.com\/setup-kubernetes-cluster-kubeadm\/\" rel=\"noreferrer\">Kubernetes cluster.<\/a><\/p>\n<p>By the end of this tutorial, you will have learned the following.<\/p>\n<ol>\n<li>What is Istio Ambient Mode and how it differs from traditional <a href=\"https:\/\/devopscube.com\/setup-istio-on-kubernetes\/\" rel=\"noreferrer\">sidecar architecture<\/a><\/li>\n<li>Install Istio Ambient Mode using <a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm charts<\/a><\/li>\n<li>Core components (ztunnel, waypoint proxy, Istio CNI) and their specific roles<\/li>\n<li>How ztunnel handles L4 traffic at the node level for all pods.<\/li>\n<li>Deploy and configure waypoint proxies using Gateway API resources to handle L7 traffic<\/li>\n<\/ol>\n<p>and more..<\/p>\n<p>Lets gets started.<\/p>\n<h2 id=\"what-is-istio-ambient-mode\">What is Istio Ambient Mode?<\/h2>\n<p><strong>Istio Ambient Mode<\/strong> is a sidecar-less architecture for the Istio <a href=\"https:\/\/devopscube.com\/service-mesh-tools\/\" rel=\"noreferrer\">service mesh<\/a>. Traditionally, Istio injects a sidecar in to every pod that is part of the mesh.<\/p>\n<p>However, Ambient Mode uses the new data plane architecture in<strong> <\/strong>Istio service mesh that<strong> does not require sidecar proxies<\/strong> for each workload. <\/p>\n<p>Instead, all the <strong>L4 traffic <\/strong>is managed by a light weight <strong>ztunnel <\/strong>(Zero Trust tunnel) proxy at the node level. Meaning all the ingress and egress connectivity from pods are intercepted by the <strong>ztunnel<\/strong> proxy.<\/p>\n<p>All the the <strong>L7 traffic<\/strong> is handled by the waypoint proxies at namespace level.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image.png\" class=\"kg-image\" alt=\"Istio Ambient Mode Architecture\" loading=\"lazy\" width=\"2000\" height=\"1558\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/01\/image.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/01\/image.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/01\/image.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2026\/01\/image.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>By eliminating per-pod sidecar proxies, it significantly <strong>reduces CPU and memory consumption<\/strong> (up to 70% savings).<\/p>\n<p>Now that you have an idea about Ambient mode, lets get started with the setup.<\/p>\n<h2 id=\"istio-ambient-mode-installation-via-helm-step-by-step\">Istio Ambient Mode Installation via Helm (Step-by-Step)<\/h2>\n<p>For this setup, we use will use the official <a href=\"https:\/\/devopscube.com\/create-helm-chart\/\" rel=\"noreferrer\">Helm<\/a> chart to install the Istio ambient mesh components one by one.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">If you want to install all components using a single chart, you can use the <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istio\/ambient<\/code> chart. This is mainly useful for testing purposes<\/p>\n<p>You can also set up the whole Ambient environment using <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">Istioctl install --set profile=ambient<\/code><\/div>\n<\/div>\n<p>Lets get started with the setup.<\/p>\n<h3 id=\"step-1-initialize-the-istio-repository\">Step 1: Initialize the Istio Repository<\/h3>\n<p>The first step is adding the whole Istio repo on our local machine and using the update command to ensure that we have all the latest charts.<\/p>\n<pre><code class=\"language-bash\">helm repo add istio https:\/\/istio-release.storage.googleapis.com\/charts \n\nhelm repo update<\/code><\/pre>\n<p>The installation begins with the base chart.<\/p>\n<h3 id=\"step-2-install-istio-base-chart\">Step 2: Install Istio Base Chart <\/h3>\n<p>The Istio base chart contains all the Istio-related Custom Resource Definitions.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/11\/image-97.png\" class=\"kg-image\" alt=\"selecting the chart for the istio base\" loading=\"lazy\" width=\"2000\" height=\"1218\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/11\/image-97.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/11\/image-97.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/11\/image-97.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2025\/11\/image-97.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>To install the base chart, use the following command.<\/p>\n<pre><code class=\"language-bash\">helm install istio-base istio\/base -n istio-system --create-namespace --wait<\/code><\/pre>\n<p>Once the installation is completed, use the following command to list them.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds | grep istio.io\n\nauthorizationpolicies.security.istio.io         2025-11-28T04:33:38Z\ndestinationrules.networking.istio.io            2025-11-28T04:33:39Z\nenvoyfilters.networking.istio.io                2025-11-28T04:33:38Z\ngateways.networking.istio.io                    2025-11-28T04:33:38Z\npeerauthentications.security.istio.io           2025-11-28T04:33:38Z\nproxyconfigs.networking.istio.io                2025-11-28T04:33:38Z\nrequestauthentications.security.istio.io        2025-11-28T04:33:38Z\nserviceentries.networking.istio.io              2025-11-28T04:33:38Z\nsidecars.networking.istio.io                    2025-11-28T04:33:38Z\ntelemetries.telemetry.istio.io                  2025-11-28T04:33:38Z\nvirtualservices.networking.istio.io             2025-11-28T04:33:38Z\nwasmplugins.extensions.istio.io                 2025-11-28T04:33:38Z\nworkloadentries.networking.istio.io             2025-11-28T04:33:38Z\nworkloadgroups.networking.istio.io              2025-11-28T04:33:38Z<\/code><\/pre>\n<p>The output shows that the CRDs are installed.<\/p>\n<p>Next,  we need to install the <a href=\"https:\/\/devopscube.com\/istio-ingress-kubernetes-gateway-api\/\" rel=\"noreferrer\">Gateway API<\/a> resources to create routing rules.<\/p>\n<h3 id=\"step-3-install-gateway-api-crds\">Step 3: Install Gateway API CRDs<\/h3>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">You might be used to <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">VirtualService<\/code> and <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">DestinationRule<\/code> resources. <\/p>\n<p>While Istio Ambient mesh still supports them, it heavily favors the <a href=\"https:\/\/devopscube.com\/kubernetes-gateway-api\/\" rel=\"noreferrer\"><b><strong style=\"white-space: pre-wrap;\">Kubernetes Gateway API<\/strong><\/b><\/a> for Layer 7 configuration using waypoint proxy.<\/p>\n<p>That is why we are setting up the Gateway API CRDs.<\/p><\/div>\n<\/div>\n<p>We need to use the Gateway API Custom Resources to create and configure the Waypoint proxy.<\/p>\n<p>To install the Gateway API CRDs, use the following command. Please refer <a href=\"https:\/\/gateway-api.sigs.k8s.io\/guides\/getting-started\/?ref=devopscube.com#installing-gateway-api\" rel=\"noreferrer\">official gateway API installation page<\/a> to get the latest version of the Gateway API CRDs<\/p>\n<pre><code class=\"language-bash\">kubectl apply --server-side -f https:\/\/github.com\/kubernetes-sigs\/gateway-api\/releases\/download\/v1.4.1\/standard-install.yaml<\/code><\/pre>\n<p>Once the installation is completed, use the following command to list the installed custom resource definitions.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get crds | grep gateway.networking.k8s.io\n\nbackendtlspolicies.gateway.networking.k8s.io    2025-12-17T05:26:49Z\ngatewayclasses.gateway.networking.k8s.io        2025-12-17T05:26:49Z\ngateways.gateway.networking.k8s.io              2025-12-17T05:26:50Z\ngrpcroutes.gateway.networking.k8s.io            2025-12-17T05:26:50Z\nhttproutes.gateway.networking.k8s.io            2025-12-17T05:26:51Z\nreferencegrants.gateway.networking.k8s.io       2025-12-17T05:26:52Z<\/code><\/pre>\n<p>Now, the Gateway API resources are installed, so the next step is to install the Istio Daemon with the ambient mode.<\/p>\n<h3 id=\"step-4-deploy-istiod-with-ambient-profile\">Step 4: Deploy Istiod with Ambient Profile<\/h3>\n<p>We need to install the Istiod and enable the ambient mode. We can enable the ambient mode during the installation.<\/p>\n<p>The following marked Istio chart we use to install the Istio Daemon.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/11\/image-98.png\" class=\"kg-image\" alt=\"selecting the chart for the istio daemon\" loading=\"lazy\" width=\"2000\" height=\"1218\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/11\/image-98.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/11\/image-98.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/11\/image-98.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2025\/11\/image-98.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>During the installation, we use the flag <code>--set-profile=ambient<\/code> to tell Istiod to enable the Ambient Mode feature. <\/p>\n<p>Use the following command to install istiod in ambient mode.<\/p>\n<pre><code class=\"language-bash\">helm install istiod istio\/istiod --namespace istio-system --create-namespace --set profile=ambient --wait<\/code><\/pre>\n<p>Once the installation is completed, ensure that the Istiod pod is running.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n istio-system get po,svc\n\nNAME                          READY   STATUS    RESTARTS   AGE\npod\/istiod-6547d75b5f-wd696   1\/1     Running   0          46s\n\nNAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)                                 AGE\nservice\/istiod   ClusterIP   10.100.88.243   &lt;none&gt;        15010\/TCP,15012\/TCP,443\/TCP,15014\/TCP   3h45m<\/code><\/pre>\n<p>On the next step, we need to install Istio CNI to redirect the incoming pod traffic to the traffic management component.<\/p>\n<h3 id=\"step-5-deploy-istio-cni\">Step 5: Deploy Istio CNI<\/h3>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">Istio CNI is not a full Kubernetes CNI plugin that replaces your cluster CNI. It is CNI chaining plugin that works alongside your existing cluster CNI (like Calico, Cilium, Flannel, etc.) rather than replacing it. <\/div>\n<\/div>\n<p>Istio CNI is an Ambient Mode component that inserts <strong>iptables rules<\/strong> so that pod traffic gets redirected through Istio ambient Ztunnel proxies.<\/p>\n<p>To install it, we need to choose the <code>istio\/cni<\/code> chart from the repo.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/11\/image-96.png\" class=\"kg-image\" alt=\"selecting the istio cni from the istio helm repo\" loading=\"lazy\" width=\"2000\" height=\"1218\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/11\/image-96.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/11\/image-96.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/11\/image-96.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2025\/11\/image-96.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Install the Istio CNI using the following helm command.<\/p>\n<pre><code class=\"language-bash\">helm install istio-cni istio\/cni -n istio-system --set profile=ambient --wait\n<\/code><\/pre>\n<p>Once the installation is completed, we need to list the <a href=\"https:\/\/devopscube.com\/troubleshoot-kubernetes-pods\/\" rel=\"noreferrer\">Pods<\/a> of the Istio CNI<\/p>\n<pre><code class=\"language-bash\">$ kubectl get all -n istio-system -l app.kubernetes.io\/instance=istio-cni\n\nNAME                       READY   STATUS    RESTARTS   AGE\npod\/istio-cni-node-br6pd   1\/1     Running   0          3m47s\npod\/istio-cni-node-f58ck   1\/1     Running   0          3m47s\n\nNAME                            DESIRED   CURRENT   READY   UP-TO-DATE   AVAILABLE   NODE SELECTOR            AGE\ndaemonset.apps\/istio-cni-node   2         2         2       2            2           kubernetes.io\/os=linux   3m48s<\/code><\/pre>\n<h3 id=\"step-6-deploy-ztunnel\">Step 6: Deploy Ztunnel<\/h3>\n<p>The final component is the Ztunnel proxy.<\/p>\n<p>The Ztunnel proxy is an Istio data plane component in Ambient mode that is the similar to the sidecar containers in the sidecar mode.<\/p>\n<p>To install Ztunnel, you need to choose the <code>istio\/ztunnel<\/code> chart from the repo.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/11\/image-136.png\" class=\"kg-image\" alt=\"selecting the ztunnel chart from the istio repo\" loading=\"lazy\" width=\"2000\" height=\"1235\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/11\/image-136.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/11\/image-136.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/11\/image-136.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2025\/11\/image-136.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Install Ztunnel using the following command.<\/p>\n<pre><code class=\"language-bash\">helm install ztunnel istio\/ztunnel -n istio-system --wait<\/code><\/pre>\n<p>Once the installation is done, use the following command to list the Ztunnel pods.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get all -n istio-system | grep -i ztunnel\n\npod\/ztunnel-frjjv             1\/1     Running   0          79s\npod\/ztunnel-jzzx2             1\/1     Running   0          79s\n\ndaemonset.apps\/ztunnel          2         2         2       2            2           kubernetes.io\/os=linux   80s<\/code><\/pre>\n<p>As you can see in the output, the ztunnel is deployed as a <a href=\"https:\/\/devopscube.com\/kubernetes-daemonset\/\" rel=\"noreferrer\">Daemonset<\/a>.<\/p>\n<p>Using  <code>istioctl<\/code> we can verify if the Ztunnel (data planes) are linked with the Istio Daemon (control plane)<\/p>\n<pre><code class=\"language-bash\">$ istioctl proxy-status \n\nNAME                           CLUSTER        ISTIOD                      VERSION     SUBSCRIBED TYPES\nztunnel-flpfw.istio-system     Kubernetes     istiod-6547d75b5f-99tbh     1.28.0      2 (WADS,WDS)\nztunnel-mzmtj.istio-system     Kubernetes     istiod-6547d75b5f-99tbh     1.28.0      2 (WADS,WDS)<\/code><\/pre>\n<p>In the output, you can see two subscriptions, such as <code>WADS<\/code> and <code>WDS<\/code>. It means, the control plane is <strong>actively &#8220;listening&#8221; for live updates<\/strong>.<\/p>\n<p>Now, the entire Istio Ambient Mode setup is ready. Next step is to create a namespace and enable ambient mode in it. <\/p>\n<h2 id=\"enabling-ambient-mode-for-workloads\">Enabling Ambient Mode for Workloads<\/h2>\n<p>To make workloads part of the Istio ambient mesh, we need to enable it in the namespace level using the <code>istio.io\/dataplane-mode=ambient<\/code> label. This way all the pods that gets deployed in that namespace will be part of the mesh.<\/p>\n<p>Lets create a <strong><code>istio-test<\/code><\/strong> namespace where we can deploy sample workloads for testing.<\/p>\n<pre><code class=\"language-bash\">kubectl create ns istio-test<\/code><\/pre>\n<p>Use the following command to set the Ambient Mode label to the namespace.<\/p>\n<pre><code class=\"language-bash\">kubectl label namespace istio-test istio.io\/dataplane-mode=ambient\n<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-yellow\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">If you want to enable the Ambient mode for only one Pod, then you can use the same label to label only the pod.<\/div>\n<\/div>\n<p>Now, verify the label using the following command.<\/p>\n<pre><code>$ kubectl get ns -L istio.io\/dataplane-mode\n\nNAME              STATUS   AGE   DATAPLANE-MODE\ndefault           Active   22h   \nistio-system      Active   21h   \nistio-test        Active   23s   ambient<\/code><\/pre>\n<p>We have enabled the ambient mode for the istio-test namespace. So any application we deploy in it will automatically be part of the Ambient mesh.<\/p>\n<p>The next step is <strong>deploy workloads<\/strong> in the ambient mesh enabled namespace for testing.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">We can still use the Sidecar Mode is Ambient setup. For that, we only need use the sidecar label (<code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istio-injection=enabled<\/code>) in that particular namespace.<\/div>\n<\/div>\n<h2 id=\"deploy-demo-applications\">Deploy Demo Applications<\/h2>\n<p>To test all the ambient mode features, we will deploy two sample apps (v1 &amp; v2) that expose a simple API.<\/p>\n<p>The following diagram illustrates l4 and l7 traffic workflow that we are going to test with the sample deployments.<\/p>\n<figure class=\"kg-card kg-image-card kg-card-hascaption\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-5.png\" class=\"kg-image\" alt=\"\" loading=\"lazy\" width=\"2000\" height=\"1947\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/01\/image-5.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/01\/image-5.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/01\/image-5.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w2400\/2026\/01\/image-5.png 2400w\" sizes=\"auto, (min-width: 720px) 720px\"><figcaption><span style=\"white-space: pre-wrap;\">l4 and l7 traffic workflow in Istio ambient mode using ztunnel and waypoint proxy<\/span><\/figcaption><\/figure>\n<p>Now lets deploy the sample APIs.<\/p>\n<p>Use the following manifest directly to create the deployments and relates services.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt;'EOF' | kubectl apply -f -\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: backend-v1\n  namespace: istio-test\nspec:\n  replicas: 3\n  selector:\n    matchLabels: { app: backend, version: v1 }\n  template:\n    metadata:\n      labels: { app: backend, version: v1 }\n    spec:\n      containers:\n      - name: echo\n        image: hashicorp\/http-echo\n        args: [\"-text=hello from backend v1\"]\n        ports:\n        - containerPort: 5678\n---\napiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: backend-v2\n  namespace: istio-test\nspec:\n  replicas: 2\n  selector:\n    matchLabels: { app: backend, version: v2 }\n  template:\n    metadata:\n      labels: { app: backend, version: v2 }\n    spec:\n      containers:\n      - name: echo\n        image: hashicorp\/http-echo\n        args: [\"-text=hello from backend v2\"]\n        ports:\n        - containerPort: 5678\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: backend-v1\n  namespace: istio-test\nspec:\n  selector:\n    app: backend\n    version: v1\n  ports:\n  - name: http\n    port: 80\n    targetPort: 5678\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: backend-v2\n  namespace: istio-test\nspec:\n  selector:\n    app: backend\n    version: v2\n  ports:\n  - name: http\n    port: 80\n    targetPort: 5678\nEOF<\/code><\/pre>\n<p>Once the deployment is completed, ensure that the deployments and services are properly created.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n istio-test get po,svc\n\nNAME                              READY   STATUS    RESTARTS   AGE\npod\/backend-v1-7c88547fc6-62skp   1\/1     Running   0          33s\npod\/backend-v1-7c88547fc6-rstb7   1\/1     Running   0          33s\npod\/backend-v1-7c88547fc6-w5s45   1\/1     Running   0          33s\npod\/backend-v2-86c767bf6b-2k8jq   1\/1     Running   0          32s\npod\/backend-v2-86c767bf6b-2nl4d   1\/1     Running   0          32s\n\nNAME                 TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)   AGE\nservice\/backend-v1   ClusterIP   10.100.121.209   &lt;none&gt;        80\/TCP    31s\nservice\/backend-v2   ClusterIP   10.100.227.24    &lt;none&gt;        80\/TCP    31s<\/code><\/pre>\n<p>Now that the applications are running, lets validate if the applications are configured to serve traffic via Istio mesh.<\/p>\n<h2 id=\"validate-application-mesh-configurations\">Validate Application Mesh Configurations<\/h2>\n<p>To ensure that the ambient mode is enabled for our deployed workload, describe one of the workload pods using the following command. Replace <code>&lt;BACKEND POD&gt;<\/code> with the pod name.<\/p>\n<pre><code class=\"language-bash\">kubectl -n istio-test describe po &lt;BACKEND POD&gt; | grep Annotations<\/code><\/pre>\n<p>You will get an output with annotation added by the Istio CNI as shown below. This annotation indicates that the traffic to this Pod is redirected to the Ztunnel proxy.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n istio-test describe po backend-v1-6997699946-9wlzq\n\n...\nAnnotations:      ambient.istio.io\/redirection: enabled\n...<\/code><\/pre>\n<p>Another way to validate is using the <strong><code>istioctl<\/code><\/strong> command. The following command lists the pods that are registered with <strong>ztunnel<\/strong> using <strong><code>HBONE<\/code><\/strong> protocol.<\/p>\n<pre><code class=\"language-bash\">$ istioctl ztunnel-config workload | grep HBONE\n\nistio-test   backend-v1-567bfdcf7-6rp4q          172.31.36.187 ip-172-31-40-209.us-west-2.compute.internal None     HBONE\nistio-test   backend-v1-567bfdcf7-r79hx          172.31.21.17  ip-172-31-26-127.us-west-2.compute.internal None     HBONE\nistio-test   backend-v1-567bfdcf7-tlzsx          172.31.42.98  ip-172-31-40-209.us-west-2.compute.internal None     HBONE\nistio-test   backend-v2-95d4845d7-fh2gg          172.31.38.186 ip-172-31-40-209.us-west-2.compute.internal None     HBONE\nistio-test   backend-v2-95d4845d7-nrv7g          172.31.22.17  ip-172-31-26-127.us-west-2.compute.internal None     HBONE<\/code><\/pre>\n<p>The above output means, all the listed workloads are in ambient mesh mode, and their traffic is being handled by ztunnel using the HBONE protocol.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\"><a href=\"https:\/\/istio.io\/latest\/docs\/ambient\/architecture\/hbone\/?ref=devopscube.com\" rel=\"noreferrer\"><b><strong style=\"white-space: pre-wrap;\">HBONE<\/strong><\/b><\/a> stands for <b><strong style=\"white-space: pre-wrap;\">HTTP-Based Overlay Network Environment<\/strong><\/b>. It is a tunneling protocol used to securely transport traffic between pods in the mesh.<\/div>\n<\/div>\n<p>Now we have apps running in the Istio mesh ready for testing. First, we will test the <strong>layer 4 traffic<\/strong> with ztunnel proxy.<\/p>\n<h2 id=\"testing-l4-connectivity-with-ztunnel\">Testing L4 Connectivity with Ztunnel<\/h2>\n<p>To test the l4 traffic, we will <strong>deploy a simple curl pod<\/strong> that sends request to the <code>backend-v1<\/code> service endpoint.<\/p>\n<p>The following diagram shows how traffic flows from a client pod to the destination pods via Ztunnel proxy.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-3.png\" class=\"kg-image\" alt=\"L4 Connectivity flow with Ztunnel in Istio Ambient mesh\" loading=\"lazy\" width=\"2000\" height=\"1166\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/01\/image-3.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/01\/image-3.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/01\/image-3.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-3.png 2236w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Execute the following command to generate traffic from the client pod.<\/p>\n<pre><code class=\"language-bash\">$ kubectl run test-client -n istio-test \\\n       --image=curlimages\/curl:latest \\\n       --restart=Never --rm -it \\\n       -- sh -c \"while true; do curl -s http:\/\/backend-v1 &amp;&amp; sleep 2; done\"\n\n\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v1<\/code><\/pre>\n<p>Now, keep in running.<\/p>\n<p>In a different terminal, lets check the logs of the Ztunnel proxy to see how the traffic is handled by it. <\/p>\n<p>Use the following command to stream new logs from the ztunnel DaemonSet pods<\/p>\n<pre><code class=\"language-bash\">$ kubectl logs -n istio-system daemonset\/ztunnel -f --all-containers=true\n\n2025-12-17T06:10:01.553894Z info access connection complete src.addr=172.31.42.213:45010 src.workload=\"test-client\" src.namespace=\"istio-test\" src.identity=\"spiffe:\/\/cluster.local\/ns\/istio-test\/sa\/default\" dst.addr=172.31.36.187:15008 dst.hbone_addr=172.31.36.187:5678 dst.service=\"backend-v1.istio-test.svc.cluster.local\" dst.workload=\"backend-v1-567bfdcf7-6rp4q\" dst.namespace=\"istio-test\" dst.identity=\"spiffe:\/\/cluster.local\/ns\/istio-test\/sa\/default\" direction=\"outbound\" bytes_sent=74 bytes_recv=184 duration=\"1ms\" \n\n2025-12-17T06:10:03.561041Z info access connection complete src.addr=172.31.42.213:43500 src.workload=\"test-client\" src.namespace=\"istio-test\" src.identity=\"spiffe:\/\/cluster.local\/ns\/istio-test\/sa\/default\" dst.addr=172.31.42.98:15008 dst.hbone_addr=172.31.42.98:5678 dst.service=\"backend-v1.istio-test.svc.cluster.local\" dst.workload=\"backend-v1-567bfdcf7-tlzsx\" dst.namespace=\"istio-test\" dst.identity=\"spiffe:\/\/cluster.local\/ns\/istio-test\/sa\/default\" direction=\"inbound\" bytes_sent=184 bytes_recv=74 duration=\"0ms\"\n\n<\/code><\/pre>\n<p>The log output shows that the source traffic from <strong><code>172.31.42.213<\/code><\/strong> (ie, test-client pod) is passing through the ztunnel along with the amount of data transferred and the latency.<\/p>\n<p>Now that our L4 traffic testing is done, lets validate L7 traffic using Ztunnel and Waypoint proxy.<\/p>\n<h2 id=\"advanced-l7-traffic-management-with-waypoint-proxies\">Advanced L7 Traffic Management with Waypoint Proxies<\/h2>\n<p>To handle L7 features like HTTP\/gRPC routing, Load balancing, Retries, timeouts, circuit breaking, <strong>L7 observability<\/strong> (HTTP metrics, access logs, tracing) etc., you need to have the <strong>waypoint proxy implementation.<\/strong><\/p>\n<p>The following diagram illustrates how the waypoint proxy works alongside Ztunnel to manage the L7 traffic.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-1.png\" class=\"kg-image\" alt=\"L7 Traffic Management with Istio Waypoint Proxies\" loading=\"lazy\" width=\"2000\" height=\"2036\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2026\/01\/image-1.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2026\/01\/image-1.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2026\/01\/image-1.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2026\/01\/image-1.png 2239w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>Here is how it works.<\/p>\n<p>When we generate L7 traffic from the client pod, the local <strong>ztunnel<\/strong> on the source node intercepts traffic and provides L4 functions. <\/p>\n<p>But when a request needs L7 processing, the source ztunnel wraps the raw TCP\/HTTP traffic into an <strong>HBONE tunnel<\/strong> (mTLS-encrypted over port 15008) and sends it to the Waypoint Proxy.<\/p>\n<p>The waypoint proxy then applies the L7 rules (e.g., &#8220;10% of traffic to v2) and then wraps the traffic back into a new HBONE tunnel and sends it to the <strong>destination ztunnel<\/strong> on the node where the target Pod actually lives.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">One Waypoint proxy is usually enough for an entire namespace or a service.<\/div>\n<\/div>\n<p>Now, lets get started with the waypoint proxy setup.<\/p>\n<h3 id=\"default-waypoint-gateway-class\">Default Waypoint Gateway Class<\/h3>\n<p>We need <strong>Kubernetes Gateway API<\/strong> to implement Waypoint proxies. <\/p>\n<p>The <code>GatewayClass<\/code> for waypoint proxy named <strong><code>istio-waypoint<\/code><\/strong> is automatically created when you install the Istiod in ambient mode.<\/p>\n<p>To view the waypoint <code>GatewayClass<\/code>, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get gc\n\nNAME             CONTROLLER                    ACCEPTED   AGE\nistio            istio.io\/gateway-controller   True       6h37m\nistio-remote     istio.io\/unmanaged-gateway    True       6h37m\nistio-waypoint   istio.io\/mesh-controller      True       6h37m<\/code><\/pre>\n<p>As you can see the istio-waypoint gateway class is associated with the <code>istio.io\/mesh-controller<\/code>. Meaning this is the controllers that spins up the required waypoint envoy pods.<\/p>\n<p>With the default GatewayClass in place, the next step is to create a Gateway.<br \/>The Gateway is what deploys the waypoint proxy.<\/p>\n<h3 id=\"create-a-waypoint-gateway\">Create a Waypoint Gateway<\/h3>\n<p>When we create a Gateway API <strong>Gateway object<\/strong>, a namespace-scoped Waypoint proxy is created.<\/p>\n<figure class=\"kg-card kg-image-card\"><img decoding=\"async\" src=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-34.png\" class=\"kg-image\" alt=\"The creation of the waypoint proxy in istio using gateway\" loading=\"lazy\" width=\"2000\" height=\"1337\" srcset=\"https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w600\/2025\/12\/image-34.png 600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1000\/2025\/12\/image-34.png 1000w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/size\/w1600\/2025\/12\/image-34.png 1600w, https:\/\/storage.ghost.io\/c\/5f\/2f\/5f2f4d20-2abf-4534-8d40-7aa233aedd43\/content\/images\/2025\/12\/image-34.png 2096w\" sizes=\"auto, (min-width: 720px) 720px\"><\/figure>\n<p>When creating a Gateway, we need to <strong>HBONE (Port 15008) listener. <\/strong>It handles the encrypted mesh traffic coming from the Ztunnel.<\/p>\n<p>We also need to mention the <code>GatewayClass<\/code>, that is, the default <strong><code>istio-waypoint<\/code> <\/strong>GatewayClass.<\/p>\n<p>Use the following to create the Gateway manifest with the above-mentioned details.<\/p>\n<pre><code class=\"language-yaml\">cat &lt;&lt; EOF &gt; test-waypoint.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: Gateway\nmetadata:\n  labels:\n    istio.io\/waypoint-for: all\n  name: test-waypoint\n  namespace: istio-test\nspec:\n  gatewayClassName: istio-waypoint\n  listeners:\n  - name: mesh\n    port: 15008\n    protocol: HBONE\n    allowedRoutes:\n      namespaces:\n        from: Same\nEOF<\/code><\/pre>\n<div class=\"kg-card kg-callout-card kg-callout-card-yellow\">\n<div class=\"kg-callout-emoji\">\u26a0\ufe0f<\/div>\n<div class=\"kg-callout-text\">When a Gateway object is labeled with <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istio.io\/waypoint-for: all<\/code>, all traffic from Ztunnel passes through Waypoint for both <b><strong style=\"white-space: pre-wrap;\">services<\/strong><\/b> and <b><strong style=\"white-space: pre-wrap;\">Pods<\/strong><\/b>.<\/p>\n<p>Instead of <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">all<\/code>, we can use <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">workload<\/code>, <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">services,<\/code> or even <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">none<\/code> to control this.<\/div>\n<\/div>\n<p>To apply this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f test-waypoint.yaml<\/code><\/pre>\n<p>Once the Gateway is created, verify it using the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n istio-test get gateway\n\nNAME            CLASS            ADDRESS          PROGRAMMED   AGE\ntest-waypoint   istio-waypoint   10.100.255.236   True         60s<\/code><\/pre>\n<p>As mentioned earlier, the Gateway object will deploy a envoy based waypoint proxy as Deployment with a ClusterIP service. <\/p>\n<p>Use the following command to validate it.<\/p>\n<pre><code class=\"language-bash\">$ kubectl get deploy,po,svc -n istio-test -l gateway.networking.k8s.io\/gateway-name=test-waypoint    \n\nNAME                            READY   UP-TO-DATE   AVAILABLE   AGE\ndeployment.apps\/test-waypoint   1\/1     1            1           3m4s\n\nNAME                                 READY   STATUS    RESTARTS   AGE\npod\/test-waypoint-6bf8c7b68b-6v5rc   1\/1     Running   0          3m4s\n\nNAME                    TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)               AGE\nservice\/test-waypoint   ClusterIP   10.100.255.236   &lt;none&gt;        15021\/TCP,15008\/TCP   3m5s<\/code><\/pre>\n<h3 id=\"enrolling-a-namespace-with-the-waypoint-proxy\">Enrolling a Namespace with the Waypoint Proxy<\/h3>\n<p>Now we have the waypoint proxy deployed and running in the istio namespace. To use it for our 7 based workloads, we <strong>need to enroll the namespace<\/strong>.<\/p>\n<p>To enroll a namespace with the waypoint proxy, you need to <strong>label the namespace <\/strong>with the waypoint Gateway name. In our case, the Gateway name is <strong><code>test-waypoint<\/code><\/strong><\/p>\n<p>Execute the following command to add the label to the namespace.<\/p>\n<pre><code class=\"language-bash\">kubectl label ns istio-test istio.io\/use-waypoint=test-waypoint<\/code><\/pre>\n<p>With the above Label the traffic goes <code>Client Pod<\/code> -&gt; <code>Ztunnel<\/code> -&gt; <code>Waypoint Proxy<\/code> -&gt; <code>Destination Pod<\/code>.<\/p>\n<p>Here is where it gets interesting. By labeling the namespace this way, you are <strong>essentially opting all workloads<\/strong> and services in that namespace to use the waypoint proxy. It means, now <strong>all the L4 and L7 traffic<\/strong> will be intercepted by the waypoint proxy. <\/p>\n<p>For example, a database operating on L4 will also use waypoint and <strong>adds a bit of latency.<\/strong><\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\">To optimize performance, you can limit the waypoint proxy usage by using the label <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istio.io\/use-waypoint:<\/code> over service, or pod.<\/p>\n<p>For example, if you only want to enable Waypoint for a particular service, then set lbel to that service as <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istio.io\/use-waypoint: backend-svc-1<\/code><\/p>\n<p>This way we can avoid the unnecessary pod to pod traffic routing to the waypoint proxy <\/p><\/div>\n<\/div>\n<p>Now, how do we know if Waypoint Proxy is now fully aware of and ready to manage traffic for our services?<\/p>\n<p>Well, we can check the proxy configurations using the following  <code>istioctl<\/code>  command. It will <strong>list the endpoints<\/strong> that the Waypoint Proxy is currently tracking.<\/p>\n<pre><code class=\"language-bash\">$ istioctl proxy-config endpoints deployment\/test-waypoint -n istio-test\n\nENDPOINT                                                STATUS      OUTLIER CHECK     CLUSTER\n127.0.0.1:15000                                         HEALTHY     OK                prometheus_stats\n127.0.0.1:15020                                         HEALTHY     OK                agent\nenvoy:\/\/connect_originate\/                              HEALTHY     OK                encap\nenvoy:\/\/connect_originate\/172.31.21.17:5678             HEALTHY     OK                inbound-vip|80|http|backend-v1.istio-test.svc.cluster.local\nenvoy:\/\/connect_originate\/172.31.22.17:5678             HEALTHY     OK                inbound-vip|80|http|backend-v2.istio-test.svc.cluster.local\nenvoy:\/\/connect_originate\/172.31.36.187:5678            HEALTHY     OK                inbound-vip|80|http|backend-v1.istio-test.svc.cluster.local\nenvoy:\/\/connect_originate\/172.31.38.186:5678            HEALTHY     OK                inbound-vip|80|http|backend-v2.istio-test.svc.cluster.local\nenvoy:\/\/connect_originate\/172.31.42.98:5678             HEALTHY     OK                inbound-vip|80|http|backend-v1.istio-test.svc.cluster.local\nenvoy:\/\/main_internal\/                                  HEALTHY     OK                main_internal\nunix:\/\/.\/etc\/istio\/proxy\/XDS                            HEALTHY     OK                xds-grpc\nunix:\/\/.\/var\/run\/secrets\/workload-spiffe-uds\/socket     HEALTHY     OK                sds-grpc<\/code><\/pre>\n<p>In the above output HEALTHY status means, the Waypoint is successfully communicating with these pods. It ensures that all our backends are configured with the Waypoint proxy. <\/p>\n<p>Now we can <strong>create an HTTPRoute object <\/strong>for L7 routing.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note: <\/strong><\/b>All the Gateway setup steps and the namespace enrollment using labels can be done using the following single <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istioctl<\/code> command<\/p>\n<p><code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istioctl waypoint apply -n default --enroll-namespace<\/code><\/p>\n<p>However, for a production setup, this approach is not ideal. Production environments should be <b><strong style=\"white-space: pre-wrap;\">declarative<\/strong><\/b> and <b><strong style=\"white-space: pre-wrap;\">versioned in Git<\/strong><\/b>.<\/p>\n<p>That is why we chose the <b><strong style=\"white-space: pre-wrap;\">Gateway CRD based approach<\/strong><\/b> with explicit configuration and custom labels, instead of relying on an imperative <code spellcheck=\"false\" style=\"white-space: pre-wrap;\">istioctl<\/code> command.<\/div>\n<\/div>\n<h3 id=\"create-a-shared-service-for-httproute\">Create a Shared Service for HTTPRoute<\/h3>\n<p>For our demo application, we deployed two deployments and two services to test weighted routing.<\/p>\n<p>For <code>HTTPRoute<\/code>, as discussed in the <a href=\"https:\/\/devopscube.com\/gateway-api-for-service-mesh\/\" rel=\"noreferrer\">GAMMA approach for east-west traffic<\/a>, we need a <strong>shared Service endpoint<\/strong> that can be referenced in the <code>HTTPRoute<\/code> configuration. <\/p>\n<p>Use the following command to create the manifest for <strong>shared Service<\/strong> named <code>backend<\/code> .<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF&gt; shared-svc.yaml\napiVersion: v1\nkind: Service\nmetadata:\n  name: backend\n  namespace: istio-test\nspec:\n  ports:\n  - name: http\n    port: 80\n    targetPort: 80\n  selector:\n    app: backend\nEOF<\/code><\/pre>\n<p>To apply this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f shared-svc.yaml<\/code><\/pre>\n<p>Now that we have the shared service endpoint, lets create the HTTPRoute.<\/p>\n<h3 id=\"create-an-httproute\">Create an HTTPRoute<\/h3>\n<p>Using <code>HTTPRoute<\/code>, we will perform a <strong>weighted canary deployment<\/strong> that splits traffic in a <strong>90\/10<\/strong> ratio between the demo applications we deployed.<\/p>\n<p>All these <strong>HTTP-based Layer 7 configurations<\/strong> will be handled by the waypoint proxy.<\/p>\n<p>Use the following configuration to create an <code>HTTPRoute<\/code><\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF &gt; istio-httproute.yaml\napiVersion: gateway.networking.k8s.io\/v1\nkind: HTTPRoute\nmetadata:\n  name: backend-route\n  namespace: istio-test\nspec:\n  parentRefs:\n  - name: backend\n    kind: Service\n    group: \"\"\n    namespace: istio-test\n  rules:\n  - matches:\n    - path:\n        type: PathPrefix\n        value: \/\n    backendRefs:\n    - name: backend-v1\n      port: 80\n      weight: 90\n    - name: backend-v2\n      port: 80\n      weight: 10\nEOF<\/code><\/pre>\n<p>As you can see, we have used the shared service <strong><code>backend<\/code><\/strong> as the <strong><code>parentRefs<\/code><\/strong>.<\/p>\n<p>To apply this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f istio-httproute.yaml<\/code><\/pre>\n<p>Verify the <code>HTTPRoute<\/code> using the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n istio-test get httproutes\n\nNAME            HOSTNAMES   AGE\nbackend-route               48s<\/code><\/pre>\n<p>Next we will enable access logging for <strong>Waypoint Proxy<\/strong> using the istio Telemetry CRD. It will record a detailed log entry for every single request it process.<\/p>\n<h3 id=\"enable-access-logging-using-telemetry\">Enable Access Logging Using Telemetry<\/h3>\n<p>Telemetry is one of the Istio Custom Resources that enables the logs and other observability features for all Istio-managed traffic in the proxies.<\/p>\n<p>Here, we are using this to generate the logs in Waypoint about the Layer 7 testing.<\/p>\n<p>Use the following manifest to enable access logging for <code>istio-test<\/code> namespace.<\/p>\n<pre><code class=\"language-bash\">cat &lt;&lt; EOF &gt; telemetry-log.yaml\napiVersion: telemetry.istio.io\/v1alpha1\nkind: Telemetry\nmetadata:\n  name: enable-logs\n  namespace: istio-test\nspec:\n  accessLogging:\n    - providers:\n      - name: envoy\nEOF<\/code><\/pre>\n<p>To apply this, use the following command.<\/p>\n<pre><code class=\"language-bash\">kubectl apply -f telemetry-log.yaml<\/code><\/pre>\n<p>To list the Telemetry resources, use the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl -n istio-test get telemetries\n\nNAME          AGE\nenable-logs   7m1s<\/code><\/pre>\n<p>Now, we are ready for testing.<\/p>\n<h3 id=\"validate-layer-7-traffic-through-the-waypoint-proxy\">Validate Layer 7 Traffic Through the Waypoint Proxy<\/h3>\n<p>To generate traffic, we will use the same curl based test-client pod and send request to the shared backend service endpoint. Ideally we should see the 90\/10 traffic split in the response.<\/p>\n<p>Execute the following command.<\/p>\n<pre><code class=\"language-bash\">$ kubectl run test-client -n istio-test \\\n       --image=curlimages\/curl:latest \\\n       --restart=Never --rm -it \\\n       -- sh -c \"while true; do curl -s http:\/\/backend &amp;&amp; sleep 2; done\"\n\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v2  &lt;-- 10% Canary Traffic\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v1\nhello from backend v2  &lt;-- 10% Canary Traffic\nhello from backend v1<\/code><\/pre>\n<p>From the output, you can see that the traffic split is happening in <strong>90\/10 way<\/strong>. It means waypoint proxy is handling the traffic.<\/p>\n<p>To further verify if waypoint proxy is handling this traffic, open another terminal to watch the logs.<\/p>\n<p>Use the following command to stream the live logs of the Waypoint proxy.<\/p>\n<pre><code class=\"language-bash\">$ kubectl logs -n istio-test -l gateway.networking.k8s.io\/gateway-name=test-waypoint -f\n\n[2025-12-01T12:59:44Z] \"GET \/\" 200 via_upstream \n  src=172.31.26.126:46074 (client)\n  dst=backend-v2.istio-test.svc.cluster.local\n  hbone_target=172.31.23.1:5678\n  gateway=istio-waypoint-gateway (inbound-vip|80)\n\n[2025-12-01T12:59:46Z] \"GET \/\" 200 via_upstream\n  src=172.31.26.126:54482 (client)\n  dst=backend-v1.istio-test.svc.cluster.local\n  hbone_target=172.31.30.34:5678\n  gateway=istio-waypoint-gateway (inbound-vip|80)\n<\/code><\/pre>\n<p>The output shows that the traffic is passing through the Waypoint Proxy, where we defined the L7 policies.<\/p>\n<p>That\u2019s it! We have successfully set up Istio Ambient Mode and tested its key features, covering both Layer 4 and Layer 7 traffic.<\/p>\n<p>Now, do not leave any resources running, as they may cost you money.<\/p>\n<p>If you are done with your learning and testing, lets clean up all the Istio system components from the cluster.<\/p>\n<h2 id=\"cleanup-the-setup\">Cleanup the Setup<\/h2>\n<p>To delete Ztunnel.<\/p>\n<pre><code class=\"language-bash\">helm delete ztunnel -n istio-system<\/code><\/pre>\n<p>To delete Istio CNI.<\/p>\n<pre><code class=\"language-bash\">helm delete istio-cni -n istio-system<\/code><\/pre>\n<p>To delete the Istio daemon.<\/p>\n<pre><code class=\"language-bash\">helm delete istiod -n istio-system<\/code><\/pre>\n<p>To delete the Istio base.<\/p>\n<pre><code class=\"language-bash\">helm delete istio-base -n istio-system<\/code><\/pre>\n<h2 id=\"is-istio-ambient-mode-production-ready\">Is Istio ambient mode production ready?<\/h2>\n<p>One key question everyone has is the production readiness of ambient mode.<\/p>\n<p>Well, Ambient mode is officially <strong>Generally Available (GA).<\/strong><\/p>\n<p>In our testing on a standard EKS cluster, we noticed that the Ztunnel resource footprint was significantly lighter than the sidecars we previously implemented. Which means, the core is <strong>stable enough<\/strong> for <strong>single cluster <\/strong>production use in many scenarios today. <\/p>\n<p>That said, this does not automatically mean you should move everything to production today.<\/p>\n<p>You should <strong>validate limits and test workloads<\/strong> carefully for your exact use case before any kind of production rollout.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-emoji\">\ud83d\udca1<\/div>\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Coexistence Architecture: <\/strong><\/b>You can run both sidecar and Ambient Mode in the same cluster, where some namespaces operate in Ambient Mode and others continue to use the sidecar model.<\/div>\n<\/div>\n<p>Now the &#8220;million dollar question&#8221; <em>Is anyone actually using Ambient Mode in production today?<\/em><\/p>\n<p>Well, Tetrate, an enterprise service mesh platform has made a secure, <a href=\"https:\/\/tetrate.io\/press\/tetrate-announces-first-to-market-fips-compliant-build-of-istio-ambient-mode-developed-in-conjunction-with-us-air-force?ref=devopscube.com\" rel=\"noreferrer\">government-ready version of Istio Ambient Mode<\/a>, tested with the U.S. Air Force, and is now offering it to customers who need high security and compliance.<\/p>\n<p>Also, the Multi-cluster ambient mesh is not production ready yet. It is still in alpha. So if you are looking for multi cluster mesh, ambient mesh is not a good choice today.<\/p>\n<p>If you looking at adoption, the <a href=\"https:\/\/hub.docker.com\/r\/istio\/ztunnel?ref=devopscube.com\" rel=\"noreferrer\">ztunnel image<\/a> in dockerhub has more than 10 million downloads.<\/p>\n<div class=\"kg-card kg-callout-card kg-callout-card-blue\">\n<div class=\"kg-callout-text\"><b><strong style=\"white-space: pre-wrap;\">Note:<\/strong><\/b> If you want to set up Istio in sidecar mode, please refer to our detailed blog on <a href=\"https:\/\/devopscube.com\/setup-istio-on-kubernetes\/\" rel=\"noreferrer\">installing Istio using the sidecar mode<\/a>.<\/div>\n<\/div>\n<h2 id=\"conclusion\">Conclusion<\/h2>\n<p>This blog covered the setup and configuration of Istio Ambient Mode.<\/p>\n<p>Unlike the traditional sidecar model, Ambient Mesh follows a layered approach by separating Layer 4 and Layer 7 traffic handling.<\/p>\n<p>One important thing to give more attention to is waypoint proxy labeling. You should design your services and namespaces in a way that only workloads that really need Layer 7 features use a waypoint proxy.<\/p>\n<p>For production setups that involve multiple clusters and multiple environments, it is recommended to use Helm along with a GitOps controller like <a href=\"https:\/\/devopscube.com\/argo-cd-ultimate-guide\/\" rel=\"noreferrer\">ArgoCD<\/a> or Flux to manage Istio ambient mesh components.<\/p>\n<p>In the upcoming blogs, we will look at how to migrate from sidecars to the ambient mesh architecture.<\/p>\n<p>If you face any issues during the setup, drop a comment below. We will help you.<\/p>\n<hr>\n<p><strong>Ngu\u1ed3n:<\/strong> <a href=\"https:\/\/devopscube.com\/setup-istio-ambient-mode\/\" target=\"_blank\" rel=\"noopener noreferrer\">How to Set Up Istio in Ambient Mode (Detailed Guide) \u2014 DevOpsCube<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Source: https:\/\/devopscube.com\/setup-istio-ambient-mode\/<\/p>\n","protected":false},"author":1,"featured_media":356,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-355","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-devops"],"_links":{"self":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/355","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=355"}],"version-history":[{"count":0,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/posts\/355\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=\/wp\/v2\/media\/356"}],"wp:attachment":[{"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=355"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=355"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.ngocha.biz\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=355"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}