Istio 1.32.0 was released on 9 November 2026. It supports Kubernetes 1.33 to 1.37 and is built on the Envoy 1.40 release branch. The same week brought patch releases 1.31.4 and 1.30.8, so if you run either of those lines you have a fresh, CVE-patched base to upgrade from.
Most of 1.32 is additive. The release adds new outlier detection and load balancing options, a stricter mode for JWT validation and experimental per-route authorization on Gateway API ingress. A few changes do alter behavior on upgrade, and one of them can silently disable an EnvoyFilter. This guide covers those first, then the features worth turning on, then the upgrade itself.
Check your Kubernetes version first
The minimum supported Kubernetes version is now 1.33. Clusters still on 1.32 need a Kubernetes upgrade before Istio 1.32 goes on. Check the server version, then run istioctl x precheck with the 1.32 binary against each cluster, which looks for other problems that would block the install.
istioctl version
kubectl version
./istio-1.32.0/bin/istioctl x precheck
Changes that can break a working mesh
The 1.32 upgrade notes list three items. Two are new defaults you will barely notice. The third only matters if you opt in to a new feature, but it fails without any warning, so it deserves the most attention.
EnvoyFilter patches on the gateway RBAC filter
When you enable ENABLE_GATEWAY_API_HTTPROUTE_AUTH on istiod (more on that feature below), ingress gateways emit the HTTP filter for ALLOW authorization policies as istio.authorization.allow instead of envoy.filters.http.rbac. The DENY and AUDIT filters keep the old name. The rename lets route-scoped configuration address the ALLOW filter on its own.
Any EnvoyFilter that matches subFilter.name: envoy.filters.http.rbac in the GATEWAY context stops matching the ALLOW filter. MERGE, REPLACE and REMOVE patches simply stop applying. INSERT_BEFORE and INSERT_AFTER are worse, because the filter you were inserting is dropped from the chain instead of landing somewhere else. A gateway whose only policies use ALLOW has no filter called envoy.filters.http.rbac at all.
Istio logs nothing when a patch fails to match. To see it, set PILOT_ENVOY_FILTER_STATS=true on istiod and watch the pilot_envoy_filter_status metric. If you depend on such a patch, add a second EnvoyFilter that matches istio.authorization.allow, or leave the HTTPRoute authorization flag off. Find the candidates before you flip the flag.
kubectl get envoyfilter -A -o yaml | grep -n "envoy.filters.http.rbac"
Client-side timeouts on Kubernetes API requests
Istio components used to wait forever for a Kubernetes API response. If a load balancer or proxy dropped the return path, the controller that issued the request stalled until the process restarted. In 1.32 every request has client-side deadlines, controlled by two environment variables.
| Variable | Default | Meaning |
|---|---|---|
ISTIO_KUBE_HEADER_TIMEOUT | 90s | How long a request may wait for response headers |
ISTIO_KUBE_REQUEST_TIMEOUT | 15m | Total duration of a request with no deadline of its own |
The header timeout must stay above the API server's --request-timeout, which is 60 seconds by default. If you run an API server with a longer request timeout, raise ISTIO_KUBE_HEADER_TIMEOUT to match, or you will see requests cancelled that would have succeeded. Setting either variable to 0 restores the old unbounded behavior. Each timeout logs a warning with the method and path and increments kube_client_deadline_fired_total, which makes a good alert.
Unhealthy endpoints now reach the proxies
PILOT_AUTO_SEND_UNHEALTHY_ENDPOINTS is on by default. A bug meant it skipped unready pods selected by a ServiceEntry and unhealthy health-checked WorkloadEntry resources. In 1.32 those endpoints appear in EDS with an unhealthy status instead of being left out. They carry an unhealthy status, so this mostly changes what you see. If you read endpoint lists with istioctl proxy-config endpoint, expect more entries than before.
Smaller changes worth knowing
Several fixes change behavior you might have built on. The source_app and destination_app metric labels go back to using the pod's app label when a pod also has app.kubernetes.io/name. Since 1.29.1 they had used app.kubernetes.io/name, which was a regression. If your dashboards or alerts were written against the regressed values, some series will change name after the upgrade.
AuthorizationPolicy now matches HTTP methods case-insensitively, and the same goes for conditions on request.headers[:authority] and request.headers[host]. Both closed bypasses where a client changed the case of a value to slip past a rule. A policy that relied on case to tell requests apart (unlikely, but possible) now treats them the same.
istioctl now verifies istiod's certificate when it connects to xDS with a service account token, for example in istioctl proxy-status with --xds-address. Scripts that point it at an address whose name doesn't match the certificate will fail. Fix the address or --authority, or pass --insecure if you accept the risk. The istiod debug endpoints also stop matching proxyID as a substring, so short forms like proxyID=istio-ingressgateway must become the full <pod>.<namespace>.
Finally, the charts no longer set a GOMAXPROCS environment variable. Go 1.25 already sets it from the container's CPU limit, so this changes nothing unless you were patching that variable yourself.
New features worth turning on
Failure-rate outlier detection
Classic outlier detection in Istio counts consecutive errors. A pod that fails every other request never reaches five in a row, so it never gets ejected even at a 50 percent error rate. The new failurePercentageThreshold field ejects a host when its failure rate over the analysis interval reaches the threshold, whether the errors are consecutive or not. A second new field, outlierDetectionHttpErrorCodes, changes which status codes count as errors.
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: payments
namespace: shop
spec:
host: payments.shop.svc.cluster.local
trafficPolicy:
outlierDetection:
interval: 10s
baseEjectionTime: 30s
maxEjectionPercent: 50
failurePercentageThreshold: 30
There is a catch the release notes don't mention. Istio sets the threshold and turns enforcement on, but it leaves Envoy's other failure percentage settings at their defaults. Envoy only runs this check when the cluster has at least 5 hosts and a host has received at least 50 requests in the interval. A service with three replicas, or a quiet one, will never eject anything by failure rate. For those, keep using consecutive5xxErrors or consecutiveGatewayErrors.
Note also that outlierDetectionHttpErrorCodes only redefines what counts as an error for the consecutive counters. Ejection is still driven by consecutive5xxErrors and consecutiveGatewayErrors.
Load balancing on backend utilization
The new backendUtilization load balancer weighs endpoints by the utilization they report in ORCA load reports, using Envoy's client_side_weighted_round_robin policy. Endpoints reporting higher load get proportionally fewer requests. It only helps if your servers emit ORCA load reports. Endpoints that report nothing get equal weight, so turning it on for a service that sends no reports just gives you round robin.
apiVersion: networking.istio.io/v1
kind: DestinationRule
metadata:
name: ratings
namespace: shop
spec:
host: ratings.shop.svc.cluster.local
trafficPolicy:
loadBalancer:
backendUtilization:
weightStabilizationPeriod: 10s
weightExpirationPeriod: 3m
weightUpdatePeriod: 1s
Required JWTs
Until now, a RequestAuthentication with several jwtRules validated each token it found but let a request through with only one of them. The new presence field on a rule changes that. With presence: REQUIRED, a request missing that token gets a 401, even when another rule is satisfied. The default stays OPTIONAL, so existing policies behave as before.
apiVersion: security.istio.io/v1
kind: RequestAuthentication
metadata:
name: api-tokens
namespace: shop
spec:
selector:
matchLabels:
app: api
jwtRules:
- issuer: "https://id.example.com"
jwksUri: "https://id.example.com/.well-known/jwks.json"
presence: REQUIRED
- issuer: "https://scopes.example.com"
jwksUri: "https://scopes.example.com/.well-known/jwks.json"
fromHeaders:
- name: x-scope-token
presence: REQUIRED
When several rules are required, claims from all the validated tokens are available to AuthorizationPolicy through request.auth.claims.
Per-route authorization on ingress (experimental)
With ENABLE_GATEWAY_API_HTTPROUTE_AUTH=true on istiod, an AuthorizationPolicy can target an HTTPRoute through targetRefs. Only ALLOW and DENY actions work, the policy must sit in the route's namespace, and only ingress gateways enforce it. Waypoints and sidecars ignore these policies.
Read the semantics carefully. A route-scoped ALLOW is evaluated together with the gateway's own ALLOW policies, so it widens access on that route rather than narrowing it. Route-scoped DENY policies add to the gateway's denials and can't override them. Remember the EnvoyFilter rename above before you enable it.
Inbound retries you can tune
Sidecars have always retried inbound requests that were reset before reaching the application, with two attempts on reset-before-request. That was hardcoded. The new defaultInboundHttpRetryPolicy mesh config field lets you tune attempts, retryOn and backoff, and setting it to {} turns inbound retries off mesh-wide. It doesn't touch defaultHttpRetryPolicy, which governs outbound calls.
Upgrade with a canary revision
The Istio docs recommend revision-based canary upgrades, and they allow a jump of two minor versions, so 1.30 to 1.32 works directly. In-place upgrades must step through each minor version. The steps below use istioctl and revision tags. Revision names can't contain dots, so 1.32.0 becomes 1-32-0.
Install the new control plane
Install 1.32 next to the existing control plane with its own revision. Pass the same overlay file you used for the current install so mesh config carries over.
./istio-1.32.0/bin/istioctl install --revision=1-32-0 -f my-overlay.yaml --skip-confirmation
kubectl get pods -n istio-system -l app=istiod -L istio.io/rev
Both istiod deployments now run side by side. Nothing has moved yet, since existing sidecars stay connected to the old control plane until their pods restart.
Move one namespace first
If your namespaces use revision tags such as prod-stable, point a canary tag at the new revision and relabel one low-risk namespace to use it. If they still use istio-injection=enabled, remove that label, because it takes precedence over istio.io/rev.
istioctl tag set prod-canary --revision 1-32-0
kubectl label namespace checkout istio-injection- istio.io/rev=prod-canary --overwrite
kubectl rollout restart deployment -n checkout
istioctl proxy-status | grep "\.checkout "
Check that the restarted pods report version 1.32.0 and that error rates for that namespace look normal for a while before going further.
Move the rest
Once the canary namespace looks healthy, move the stable tag. Every namespace labeled prod-stable now injects from 1.32 on its next restart, without any relabeling.
istioctl tag set prod-stable --revision 1-32-0 --overwrite
kubectl rollout restart deployment -n shop
kubectl rollout restart deployment -n payments
Restart or upgrade the gateways too. A gateway can be pinned to a revision of its own, so check which control plane each one reports in istioctl proxy-status.
Remove the old control plane
When istioctl proxy-status shows nothing connected to the old revision, set the default tag to the new revision and uninstall the old one by its revision name (1-31-4 in this example). If the old control plane was installed without a revision, uninstall it with its original install options instead.
istioctl tag set default --revision 1-32-0 --overwrite
istioctl uninstall --revision 1-31-4 -y
If something goes wrong before this step, rolling back is cheap. Point the tags back at the old revision, restart the affected namespaces and uninstall the canary. That safety net is the main reason to prefer revisions over an in-place upgrade, even for a release as quiet as this one.