Istio How-to Guides¶
Scope
Task recipes for Istio 1.29-1.31: installing sidecar and ambient modes, Gateway API CRDs, waypoints, canary upgrades with revision tags, sidecar-to-ambient migration, traffic management, mTLS and authorization, hardening, and troubleshooting. Commands were checked against the istio.io release-1.31 docs on 2026-09-25. Look-up tables (profiles, ports, labels, versions) are in Reference; background is in Explanation.
Artifact locations changed in 1.31
Istio 1.31+ no longer publishes to gcr.io/istio-release, registry.istio.io, or istio-release.storage.googleapis.com. Use Docker Hub images, the Helm repo https://blob.istio.io/istio-release/charts, or OCI charts at ghcr.io/istio/release/charts. Update mirrors and air-gapped pipelines before the final GCP "scream test" (2026-12-08/09).
Deployment Patterns¶
Installation Profiles¶
Pick a deployment profile (default, demo, minimal, remote, ambient, empty, preview) plus an optional platform profile (gke, eks, openshift, k3s, ...). The full table of what each profile installs is in Reference: Installation Profiles and Helm Charts.
Install the Gateway API CRDs¶
Istio does not install the Kubernetes Gateway API CRDs. Install them before using Gateway/HTTPRoute or waypoints, and upgrade them before upgrading Istio (1.30 needs v1.5.x, 1.31 builds against v1.6.0):
kubectl get crd gateways.gateway.networking.k8s.io &> /dev/null || \
kubectl apply --server-side -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.6.0/standard-install.yaml
Use experimental-install.yaml instead if you need experimental-channel resources. After upgrading Istio, istioctl analyze reports IST0176 when the installed CRDs are older than the version Istio requires.
Sidecar Mode (istioctl)¶
# Production install with istioctl
istioctl install --set profile=default \
--set meshConfig.accessLogFile=/dev/stdout \
--set values.pilot.resources.requests.memory=2Gi -y
# Enable sidecar injection for a namespace, then restart workloads
kubectl label namespace default istio-injection=enabled
kubectl rollout restart deployment -n default
Auto mTLS (meshConfig.enableAutoMtls) is on by default, so it does not need to be set.
Sidecar Mode with the Istio CNI Node Agent¶
Removes the privileged istio-init container from every pod:
cat <<EOF > istio-cni.yaml
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
components:
cni:
namespace: istio-system
enabled: true
EOF
istioctl install -f istio-cni.yaml -y
With Helm: helm install istio-cni istio/cni -n istio-system --wait, then install istiod with --set pilot.cni.enabled=true.
Ambient Mesh (Sidecar-less)¶
# Install ambient mode (istiod + istio-cni + ztunnel; no ingress gateway)
istioctl install --set profile=ambient --skip-confirmation
# Add a namespace to the ambient mesh (no pod restart needed)
kubectl label namespace default istio.io/dataplane-mode=ambient
# Deploy a waypoint for L7 and enrol the namespace in it (optional)
istioctl waypoint apply -n default --enroll-namespace
istioctl waypoint status -n default
Ambient Mesh with Helm¶
helm repo add istio https://blob.istio.io/istio-release/charts
helm repo update
helm install istio-base istio/base -n istio-system --create-namespace --wait
helm install istiod istio/istiod --namespace istio-system --set profile=ambient --wait
helm install istio-cni istio/cni -n istio-system --set profile=ambient --wait
helm install ztunnel istio/ztunnel -n istio-system --wait
# Optional ingress gateway
helm install istio-ingress istio/gateway -n istio-ingress --create-namespace --wait
For native nftables instead of iptables (ambient 1.28+, sidecar 1.27+), add --set values.global.nativeNftables=true (istioctl) or the equivalent global.nativeNftables=true Helm value.
Upgrades¶
Canary Upgrade with Revision Tags¶
The recommended upgrade path runs two control-plane revisions side by side and moves a tag between them.
# 0. Check compatibility and read the upgrade notes for the target release
istioctl x precheck
# 1. Install the new control plane as a revision (no traffic moves yet)
istioctl install --revision=1-31-1 --set profile=minimal --skip-confirmation
# 2. Point stable/canary tags at revisions
istioctl tag set prod-stable --revision 1-30-5
istioctl tag set prod-canary --revision 1-31-1
# 3. Move a test namespace to the canary and restart it
kubectl label namespace test-ns istio-injection- istio.io/rev=prod-canary --overwrite
kubectl rollout restart deployment -n test-ns
istioctl proxy-status
# 4. Promote: move the stable tag, restart the remaining namespaces
istioctl tag set prod-stable --revision 1-31-1 --overwrite
kubectl rollout restart deployment -n app-ns-1
# 5. Remove the old control plane once no proxies use it
istioctl uninstall --revision 1-30-5 -y
Revision names must be DNS-label safe (use 1-31-1, not 1.31.1). The default tag (istioctl tag set default --revision 1-31-1) controls which revision serves istio-injection=enabled and cluster-wide validation.
Upgrade checklist for 1.30 and 1.31
- 1.30: upgrade Gateway API CRDs to v1.5.x first, or
TLSRoute/ReferenceGrantbecome invisible and TLS passthrough listeners showattachedRoutes: 0. XDS debug endpoints on 15010 need auth (istioctl --plaintextbreaks;ENABLE_DEBUG_ENDPOINT_AUTH=falserestores the old behaviour). CNI config files are now mode 0600. - 1.31: update artifact locations (see above). Istio now sends unhealthy endpoints unless
outlierDetection.minHealthPercentis set (PILOT_AUTO_SEND_UNHEALTHY_ENDPOINTS=falseto revert). Large ambient meshes (about 40,000+ workloads) should raiseISTIO_GPRC_MAXRECVMSGSIZEon istiod. Previously auto-registeredWorkloadEntryobjects need thenetworking.istio.io/tunnel=httplabel or re-registration to use HBONE. - Use
compatibilityVersion(for example1.30) to keep previous defaults while you test.
Upgrade with Helm¶
helm repo update
helm upgrade istio-base istio/base -n istio-system
helm upgrade istiod istio/istiod -n istio-system --set profile=ambient --wait
helm upgrade istio-cni istio/cni -n istio-system --set profile=ambient --wait
helm upgrade ztunnel istio/ztunnel -n istio-system --wait
Upgrade the CNI agent before or together with istiod; since 1.29 the agent reconciles in-pod redirect rules for existing ambient pods automatically. For revisioned Helm installs, follow the Helm upgrade guide.
Migrate from Sidecar to Ambient¶
The migration guide (added with 1.30) is gradual and reversible, one namespace at a time:
- Install ztunnel and switch istio-cni to the ambient profile; sidecar workloads keep working.
- Migrate policies:
VirtualServicetoHTTPRoute(VirtualService in ambient is alpha), subsets to version-specific Services, L7AuthorizationPolicy,RequestAuthenticationandWasmPluginto waypoints viatargetRefs. L4 policies need no change.EnvoyFilterhas no ambient equivalent. - Per namespace: deploy a waypoint if needed, label
istio.io/dataplane-mode=ambientandistio.io/use-waypoint, remove the injection label, restart pods.
L7 policy gap
With L7 policies, zero-downtime migration is not currently possible: between removing selector-based sidecar policies and applying waypoint-attached ones, L7 rules are unenforced. Plan a maintenance window.
Traffic Management¶
Canary Deployment (Istio API)¶
Subsets v2/v3 must be defined in a DestinationRule for reviews.
apiVersion: networking.istio.io/v1
kind: VirtualService
metadata:
name: reviews
spec:
hosts:
- reviews
http:
- match:
- headers:
end-user:
exact: jason
route:
- destination:
host: reviews
subset: v3
- route:
- destination:
host: reviews
subset: v2
weight: 90
- destination:
host: reviews
subset: v3
weight: 10
Circuit Breaking¶
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: backend
spec:
host: backend
trafficPolicy:
connectionPool:
tcp:
maxConnections: 100
http:
h2UpgradePolicy: DEFAULT
http1MaxPendingRequests: 100
http2MaxRequests: 1000
outlierDetection:
consecutive5xxErrors: 5
interval: 30s
baseEjectionTime: 30s
maxEjectionPercent: 50
Since 1.31, meshConfig.defaultTrafficPolicy can set a mesh-wide baseline connectionPool and outlierDetection that every DestinationRule inherits.
Security¶
Peer Authentication (mTLS)¶
A PeerAuthentication in the root namespace (istio-system) applies mesh-wide:
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT # STRICT | PERMISSIVE | DISABLE
Roll out PERMISSIVE first, confirm with telemetry that no plaintext clients remain, then switch to STRICT. For a namespace-wide policy, create the same resource in that namespace.
Harden a Multi-Tenant Mesh¶
- Restrict
EnvoyFiltercreate/update to mesh administrators with Kubernetes RBAC (ISTIO-SECURITY-2026-006). - Prefer Gateway API routes over
VirtualServicewith themeshgateway in namespace-based multi-tenancy (ISTIO-SECURITY-2026-002); limit who can createVirtualService,DestinationRule, andServiceEntry. - Restrict who can create
RequestAuthentication(JWKS SSRF, CVE-2026-41413). - Keep debug endpoint auth on (
ENABLE_DEBUG_ENDPOINT_AUTH=true, default since 1.30). - Optionally deploy default NetworkPolicies for istiod, istio-cni, and ztunnel with
global.networkPolicy.enabled=true(1.29+); allow TCP 15008 for ambient pods. - For FIPS environments on 1.31, set
COMPLIANCE_POLICY=fips-140-3. - Stay on the latest patch of a supported minor; see Reference: Security Bulletins.
Troubleshooting¶
| Symptom | Diagnosis | Fix |
|---|---|---|
| Sidecar not injected | kubectl get ns --show-labels; istioctl analyze |
kubectl label ns <ns> istio-injection=enabled (or istio.io/rev=<tag>), then restart pods |
| Ambient pod not captured | istioctl ztunnel-config workloads shows protocol TCP instead of HBONE |
Check namespace/pod labels, istio-cni agent logs, and that the pod is not in excludeNamespaces |
| 503 errors | istioctl analyze; istioctl proxy-config cluster <pod> |
Check DestinationRule subsets, VirtualService/HTTPRoute backends |
| mTLS handshake failures | istioctl proxy-config secret <pod>; istioctl ztunnel-config certificates |
Check PeerAuthentication mode and that both sides share a root |
| L7 policy ignored in ambient | istioctl waypoint status -n <ns> |
Deploy a waypoint and add istio.io/use-waypoint; attach policy with targetRefs |
| TLS passthrough stops after upgrade to 1.30 | kubectl get gateway -o yaml shows attachedRoutes: 0; istioctl analyze IST0176 |
Upgrade Gateway API CRDs to v1.5.x+ |
ztunnel reconnect loop, ResourceExhausted (1.31, very large meshes) |
istiod logs | Raise ISTIO_GPRC_MAXRECVMSGSIZE on istiod |
| High latency | istioctl proxy-status; Envoy stats |
Check proxy CPU limits and worker threads; scope config with Sidecar |
| Config rejected | istioctl validate -f config.yaml |
Fix YAML and apiVersion |
# Debug toolkit
istioctl analyze --namespace default
istioctl proxy-status
istioctl proxy-config routes <pod>
istioctl proxy-config listeners <pod>
kubectl logs -l app=istiod -n istio-system
Resource Requirements¶
Starting resource requests and the official per-proxy measurements are in Reference: Resource and Performance Figures. Size istiod from the number of proxies and the rate of config changes, and scale it horizontally.
Commands & Recipes¶
Installation (Ambient Mode)¶
# Download istioctl (pin a version; the script defaults to the latest release)
curl -L https://istio.io/downloadIstio | ISTIO_VERSION=1.31.1 sh -
export PATH=$PWD/istio-1.31.1/bin:$PATH
# Install the ambient profile
istioctl install --set profile=ambient -y
# Enable ambient for a namespace
kubectl label namespace default istio.io/dataplane-mode=ambient
# Verify
istioctl version
kubectl get pods -n istio-system
Traffic Management (Gateway API)¶
# Canary deployment (90/10 split) for in-mesh traffic (GAMMA: parentRef is a Service)
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: myapp-canary
spec:
parentRefs:
- group: ""
kind: Service
name: myapp
port: 8080
rules:
- backendRefs:
- name: myapp-v1
port: 8080
weight: 90
- name: myapp-v2
port: 8080
weight: 10
In ambient mode this route is enforced by the waypoint serving myapp, so myapp (or its namespace) must use a waypoint.
# Waypoint proxy (opt-in L7 for a namespace); equivalent to `istioctl waypoint apply -n default`
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: waypoint
namespace: default
labels:
istio.io/waypoint-for: service
spec:
gatewayClassName: istio-waypoint
listeners:
- name: mesh
port: 15008
protocol: HBONE
Security Policies¶
# PeerAuthentication: require mTLS mesh-wide
apiVersion: security.istio.io/v1
kind: PeerAuthentication
metadata:
name: default
namespace: istio-system
spec:
mtls:
mode: STRICT
# AuthorizationPolicy: L7 rules (sidecar mode: selector; ambient: use targetRefs to the waypoint/Service)
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
name: allow-frontend
spec:
selector:
matchLabels:
app: backend
action: ALLOW
rules:
- from:
- source:
principals: ["cluster.local/ns/default/sa/frontend"]
to:
- operation:
methods: ["GET"]
paths: ["/api/*"]
Diagnostics¶
# Proxy sync status
istioctl proxy-status
# Analyze configuration issues in all namespaces
istioctl analyze -A
# Debug Envoy config for a workload
istioctl proxy-config routes deploy/myapp
istioctl proxy-config clusters deploy/myapp
# Ambient: what ztunnel knows
istioctl ztunnel-config workloads
istioctl ztunnel-config certificates
kubectl logs -n istio-system -l app=ztunnel -f