Getting Started with Istio: A Hands-On Tutorial (2026)
A practical walkthrough to install Istio, turn on automatic mTLS, and run a canary traffic split on Kubernetes.
Key takeaways
A practical walkthrough to install Istio, turn on automatic mTLS, and run a canary traffic split on Kubernetes.
On this page
Istio has a reputation for being heavy, and some of that is earned. But you don't need to understand every CRD to get real value out of it. This tutorial walks through the parts that actually matter on day one: getting the mesh installed, proving that mTLS is on, and shifting traffic between two versions of a service. Everything below is meant to be run, not just read.
Istio moves fast, so treat the commands here as a starting shape and verify against current Istio releases. Profile names, default versions, and Gateway API support all shift between minor versions.
If you want the bigger picture of where a mesh fits, start with our service mesh guide. If you're still deciding on a mesh, the Istio vs Linkerd comparison is worth a read before you commit.
Prerequisites#
You need three things:
- A running Kubernetes cluster. A local
kindorminikubecluster with at least 4 GB of RAM works fine for this. Managed clusters (EKS, GKE, AKS) are all fine too. kubectlconfigured and pointing at that cluster. Confirm withkubectl get nodes.- Cluster-admin permissions, since installing Istio creates cluster-scoped resources and CRDs.
That's it. No Helm required for the quick path, though I'll mention it.
Installing Istio#
Download istioctl and put it on your PATH:
curl -L https://istio.io/downloadIstio | sh -
cd istio-*/
export PATH=$PWD/bin:$PATH
istioctl version
Now pick a profile. Istio has two data-plane modes worth knowing about:
- Sidecar: the classic model. Every pod gets an Envoy proxy injected next to it. Mature, well understood, and what most tutorials assume.
- Ambient: the newer sidecar-less mode. A per-node
ztunnelhandles L4 and mTLS, and you addwaypointproxies only where you need L7 features. Lower per-pod overhead, but newer.
We'll use the sidecar path here because it's the most portable across versions. Install the demo profile, which turns on the ingress gateway and generous telemetry:
istioctl install --set profile=demo -y
kubectl get pods -n istio-system
You should see istiod and istio-ingressgateway running. If you'd rather manage Istio through GitOps, the Helm charts (istio/base, istiod, istio/gateway) install the same components and are the better fit for production. The Gateway API path uses the same istioctl install but swaps Gateway/HTTPRoute resources for the older ingress gateway. Either works.
Enabling sidecar injection and deploying a sample app#
Injection is opt-in per namespace. Label a namespace and Istio's mutating webhook adds the proxy to every new pod:
kubectl create namespace demo
kubectl label namespace demo istio-injection=enabled
Now deploy the Bookinfo sample that ships with the release. It's a small app with a productpage frontend and three versions of a reviews service, which makes it perfect for a traffic-split demo:
kubectl apply -n demo -f samples/bookinfo/platform/kube/bookinfo.yaml
kubectl get pods -n demo
Watch the READY column. Each pod should show 2/2 once it settles: one container for your app, one for the injected Envoy proxy. If you see 1/1, injection didn't happen, and we'll cover why below.
Verifying automatic mTLS#
Here's the part people don't believe until they see it. Istio turns on mTLS between proxies automatically, in permissive mode by default, so plaintext still works during migration. To require it, apply a PeerAuthentication in STRICT mode:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: demo
spec:
mtls:
mode: STRICT
kubectl apply -f peer-auth.yaml
Now every connection between meshed pods in demo is mutually authenticated and encrypted, and plaintext connections are rejected. You can confirm what a workload negotiated with:
istioctl x describe pod -n demo <productpage-pod-name>
Look for a line reporting that mTLS is enabled. This is the single highest-value thing Istio gives you for the least effort.
Traffic management: a 90/10 canary split#
Bookinfo ships three versions of reviews. Without any rules, Kubernetes load-balances across all of them randomly. Let's take control and send 90% of traffic to v1 and 10% to v2.
First a DestinationRule to name the subsets, then a VirtualService to weight them:
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: reviews
namespace: demo
spec:
host: reviews
subsets:
- name: v1
labels:
version: v1
- name: v2
labels:
version: v2
---
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
namespace: demo
spec:
hosts:
- reviews
http:
- route:
- destination:
host: reviews
subset: v1
weight: 90
- destination:
host: reviews
subset: v2
weight: 10
Apply it, then refresh the product page a few dozen times. Roughly one in ten requests should hit v2. To promote the canary, bump v2 to weight: 100 and re-apply. No redeploys, no pod restarts.
If you're on the Gateway API path, the same split is expressed as an HTTPRoute with backendRefs carrying weight fields pointing at two Services. The mechanics are identical; only the resource shape differs.
Viewing telemetry#
The demo profile makes wiring up dashboards trivial. Install the sample addons and open Kiali:
kubectl apply -f samples/addons/
istioctl dashboard kiali
Kiali gives you a live service-graph with request rates, error rates, and a lock icon on edges where mTLS is active. Prometheus scrapes the metrics, and Grafana has prebuilt Istio dashboards (istioctl dashboard grafana) for latency percentiles and throughput per workload. For a first look, drive some traffic to productpage and watch the graph in Kiali light up.
Common gotchas#
- Sidecar not injected: the most frequent issue. The namespace label must be present before pods start. If you labeled after deploying, restart the workloads:
kubectl rollout restart deployment -n demo. - STRICT mTLS breaking plaintext clients: anything outside the mesh talking directly to a meshed pod on the plaintext port will get rejected under STRICT. Migrate with
PERMISSIVEmode first, confirm all clients are meshed, then flip to STRICT. - Resource overhead: each sidecar adds CPU and memory. On dense clusters this adds up. This is exactly the pressure ambient mode is designed to relieve, so evaluate it if per-pod cost is a concern.
Uninstalling cleanly#
Tear down in reverse order so you don't strand resources:
kubectl delete -f samples/addons/ --ignore-not-found
kubectl delete namespace demo
istioctl uninstall --purge -y
kubectl delete namespace istio-system
The --purge flag removes the CRDs too, so only use it when you truly want Istio gone.
Next steps#
Once the basics click, the natural next moves are: fault injection and request timeouts (both live in VirtualService), authorization policies to lock down which services can call which, and multi-cluster mesh if you're spanning regions. If per-pod overhead bothered you, spend an afternoon with ambient mode on a throwaway cluster.
The call we'd make#
For a first install, use the sidecar demo profile, prove mTLS with a STRICT PeerAuthentication, and run one canary split. That combination shows you the two things Istio is genuinely good at, security you didn't have to code and traffic control you didn't have to redeploy for, without drowning you in CRDs. Reach for ambient and the Gateway API path once the fundamentals feel routine, and keep validating every command against the release you actually run.
Get the DevOps Troubleshooting Cheat Sheet
Subscribe and get our free one-page reference for the errors that eat an afternoon — CrashLoopBackOff, OOMKilled, Terraform state locks, and more — plus new guides as we publish them.
AWS Security Best Practices Checklist (2026)
A practitioner's checklist for securing AWS, ordered by impact so you fix the things attackers actually exploit first.
Terraform count vs for_each (and the Errors)
A practical look at when to reach for count, when for_each saves you, and the specific errors each meta-argument tends to throw.
More from DevOps
Explore more articles in this category
Best Log Management Tools in 2026: What You Actually Pay For
Every log platform looks affordable at proof-of-concept volume and expensive at production volume. The pricing model, not the feature list, decides which one you can live with.
Best Managed Kubernetes in 2026: EKS vs GKE vs AKS vs DOKS
The control plane fee is the least interesting number. What separates managed Kubernetes providers is upgrade cadence, how much they run for you, and where the node bill lands.
Your CI Runner Is the Target: Hardening Against npm Worms
The keyv compromise reached 444 packages and over two billion monthly installs through preinstall scripts. The controls that actually stop it are boring and mostly free.
You might have missed
Evergreen posts worth revisiting.