Domain 1: Installation, Upgrades and Configuration
- Istio installs with istioctl or with Helm, and whichever tool created the install is the one that must manage it: neither adopts the other objects, so mixing them leaves the release state and the cluster disagreeing.
- With Helm the base chart installs the custom resource definitions and cluster-wide roles and must go in before the istiod chart; a control-plane upgrade that skips it leaves the cluster rejecting resources the new control plane understands.
- istioctl manifest generate renders everything an install would apply without applying it, which is how a profile is reviewed or diffed; istioctl x precheck reports whether the cluster can be installed onto, and verify-install compares a finished install against its manifest.
- Installation profiles decide what is deployed: demo for evaluation (it pulls in extra components not recommended for production), default as the production baseline, minimal for the control plane alone, and ambient for the sidecar-less data plane.
- Sidecar mode is enabled by labelling a namespace istio-injection=enabled, or istio.io/rev=<revision> for a revision-based install; a mutating admission webhook then injects the proxy when a pod is created.
- Existing pods are never injected retroactively. After labelling a namespace the workloads have to be restarted, for example with kubectl rollout restart, before the webhook can act on them.
- Ambient mode is the sidecar-less data plane: a per-node ztunnel DaemonSet provides L4 connectivity and mTLS, and a waypoint proxy is deployed only for the namespaces or service accounts that actually need L7 features such as HTTP routing and L7 authorization.
- Pods with hostNetwork: true are skipped by injection, because programming iptables in the host network namespace would redirect traffic for every process on the node.
- Proxy behaviour is configured in layers: meshConfig.defaultConfig is the mesh-wide default for every sidecar, and a proxy.istio.io/config annotation on a pod template overrides it for that workload alone.
- Interception is programmed per pod, so exclusions are pod annotations: inbound and outbound port ranges can be excluded from capture for a workload that manages its own connections.
- The install namespace and the root configuration namespace are separate settings and must name the same namespace, or mesh-wide defaults applied to the root namespace are silently never read.
- Canary upgrades install a new istiod alongside the old one under a distinct revision, so namespaces move one at a time by changing their revision label and restarting; an in-place upgrade replaces the control plane in a single step with nothing validated first.
- A revision tag is a stable name that namespaces point at, so a group of namespaces moves by repointing the tag once rather than relabelling each namespace. Istio is upgraded one minor version at a time - skipping a version is outside the supported path.
- The control plane upgrades first and keeps serving the previous data plane within the supported skew; gateways are ordinary deployments and do not move until their own deployment is rolled, which is the step most upgrade plans forget.
Domain 2: Traffic Management
- A VirtualService defines how requests are routed to a service (host matching, weighted splits, header/URI matches, retries, timeouts, fault injection, mirroring); a DestinationRule defines policies applied after routing (subsets, load balancing, connection pools, outlier detection, TLS).
- Subsets are declared in a DestinationRule under spec.subsets, each with a name and labels (e.g. version: v1); a VirtualService route then targets destination.host plus destination.subset.
- Weighted/canary routing is done with http.route[] entries each carrying a weight value (e.g. 90 to v1, 10 to v2); weights across a route must sum to 100.
- Header-based routing uses http.match[].headers (e.g. end-user.exact: tester). Match rules are evaluated top to bottom, so put specific matches (like the header route to v2) before the catch-all weighted default.
- Retries are configured with http.retries: { attempts, perTryTimeout, retryOn } (e.g. attempts: 3, perTryTimeout: 2s, retryOn: "5xx,connect-failure").
- Timeouts are set per-route with http.timeout; this is the overall request timeout, distinct from a retry's perTryTimeout.
- Fault injection is defined in a VirtualService: http.fault.delay (fixedDelay plus percentage) injects latency, and http.fault.abort returns synthetic HTTP error codes - both used for resilience testing.
- Traffic mirroring (shadowing) copies live requests to another destination via http.mirror plus mirrorPercentage.value; mirrored traffic is fire-and-forget and the response from the mirror is ignored.
- A Gateway resource configures an Istio ingress/egress proxy at the mesh edge (ports, protocols, TLS); it must be bound to a VirtualService to actually route traffic to backend services.
- Server TLS on a Gateway uses tls.mode: SIMPLE (or MUTUAL) with credentialName referencing a Kubernetes TLS secret holding the cert and key.
- Load-balancing policy lives in a DestinationRule trafficPolicy.loadBalancer: ROUND_ROBIN, LEAST_REQUEST, RANDOM, or consistentHash (using httpHeaderName, httpCookie, or useSourceIp for session affinity).
- Circuit breaking combines DestinationRule connectionPool limits (max connections/requests) with outlierDetection, which ejects hosts returning consecutive errors (e.g. consecutive5xxErrors) for a baseEjectionTime.
- External (off-mesh) destinations require a ServiceEntry to register them in the mesh registry; controlling egress to those hosts may also use an egress Gateway.
- A typical canary workflow: define v1/v2 subsets in a DestinationRule, start a VirtualService at 95/5, gradually shift weight, then move 100% to v2 once healthy.
Domain 3: Securing Workloads
- Istio assigns each workload a SPIFFE identity derived from its Kubernetes service account, formatted spiffe://<trust-domain>/ns/<namespace>/sa/<serviceaccount>. Two workloads sharing a service account are indistinguishable to both mTLS and authorization policy.
- istiod acts as the certificate authority and issues short-lived X.509 certificates that the agent beside each proxy rotates automatically, so a stolen certificate is valid only briefly and there is no manual rotation step.
- PeerAuthentication sets the mTLS mode a workload accepts inbound: STRICT accepts only mTLS, PERMISSIVE accepts both plaintext and mTLS, and DISABLE turns it off.
- A mesh-wide policy is a PeerAuthentication with no workload selector applied in the root configuration namespace; both conditions matter, because elsewhere it is only namespace policy.
- A staged migration runs STRICT as the mesh default with PERMISSIVE overrides on the namespaces that still have non-mesh callers, so every exception is explicit and is removed as its callers move inside the mesh.
- portLevelMtls overrides the mode for individual ports, which is how a plaintext metrics port is exempted while the workload stays STRICT.
- PeerAuthentication governs what a server accepts inbound; the DestinationRule tls setting governs what a client sends outbound. Confusing the two is why disabling TLS on a DestinationRule never makes a STRICT server accept plaintext.
- Under STRICT, kubelet probes fail because they are plaintext callers from outside the mesh. Istio can rewrite HTTP probes so they arrive through the sidecar, or the probe port can be excluded from interception - relaxing the mTLS mode is the wrong fix.
- AuthorizationPolicy enforces access control with action ALLOW, DENY, AUDIT or CUSTOM. Once any ALLOW policy selects a workload, only requests matching an allow rule are permitted and everything else is implicitly denied.
- Evaluation order is CUSTOM, then DENY, then ALLOW: a matching DENY rejects the request whatever the ALLOW policies say, which is why a broad DENY cannot be carved out later.
- Rules match callers on from.source.principals (the proved SPIFFE identity), on namespaces and IP blocks, and match the request itself on to.operation paths and methods, with negative forms such as notPaths for exceptions.
- RequestAuthentication validates end-user JWTs, locating the token with fromHeaders and the keys with jwksUri, but it rejects only invalid tokens - a request with no token is merely unauthenticated. Requiring a token is an AuthorizationPolicy matching requestPrincipals.
- Edge TLS is configured on the Gateway server entry: SIMPLE terminates using the server certificate named by credentialName, MUTUAL also verifies a client certificate, and PASSTHROUGH forwards the connection encrypted with routing driven by the requested name.
- A gateway reads its credential from the namespace its own deployment runs in, not from the application namespace, and credentials are delivered dynamically so a renewed certificate is picked up without restarting the gateway.
Domain 4: Troubleshooting
- istioctl analyze validates Istio configuration and reports errors and warnings without the resources needing to be applied, so it runs against manifests in CI and catches a host that will not resolve or a Sidecar scope that is missing.
- Admission validates the schema only. A resource that is well formed but refers to nothing - a subset with no DestinationRule, a host with no service - is stored happily and then has no effect, which is the quiet failure analyze and a proxy dump exist to find.
- A DestinationRule subset is only a label selector, so a subset whose labels match no pod resolves to zero endpoints and traffic to it fails while the configuration looks correct.
- A host written without a full domain resolves relative to the namespace of the resource that names it, not the caller, so the same short host means different things depending on where the resource lives.
- exportTo controls which namespaces can see a VirtualService, DestinationRule or ServiceEntry, so a valid resource can simply be invisible to the callers that needed it.
- A Gateway is attached to its workload by a selector; a selector matching no labels produces valid configuration that is never programmed onto any proxy, and a VirtualService naming a gateway in another namespace must qualify it with that namespace.
- istioctl proxy-status compares what istiod has sent with what each proxy has acknowledged. STALE for a resource type means that proxy has not confirmed the latest push, so a single stale proxy points at that connection rather than at the configuration.
- Relabelling a namespace to a new revision only changes what will be injected next time; running pods keep the proxy they were injected with and stay attached to the old control plane until their deployments are rolled.
- A pod that starts without its sidecar in an injection-enabled namespace usually means the mutating webhook did not run - istiod unreachable, the webhook certificate expired, or the namespace excluded by a selector - not that the configuration is wrong.
- istioctl proxy-config prints what a named pod proxy actually received: listeners, routes, clusters, endpoints and secrets. It is the only way to see the effective configuration rather than the configuration that was intended.
- Envoy response flags name the failure: NR means no route matched, UF and UC mean the upstream connection failed or was terminated, UT means the upstream request timed out, DC means the downstream client closed the connection, and UO means the circuit breaker shed the request.
- A route timeout bounds the whole request including retries while a per-try timeout bounds one attempt, so a retry policy under a small route timeout silently loses its later attempts.
- The Envoy admin interface on port 15000 of each sidecar serves /config_dump and the proxy statistics, and 15006 and 15001 are the virtual inbound and outbound capture ports that iptables redirects to.
- Request metrics are reported by the proxies that handled the traffic, each with its own reporter label, so traffic visible only under the source reporter means the destination workload has no sidecar rather than that the metrics pipeline is broken.
Istio Certified Associate (ICA) exam tips
- The exam is hands-on and command-heavy: practice istioctl (install, kube-inject, analyze, proxy-config, proxy-status, x describe) and kubectl until applying and debugging YAML is muscle memory under time pressure.
- Memorize which resource owns which job: VirtualService = routing/retries/timeouts/fault/mirror; DestinationRule = subsets/load-balancing/connection-pools/outlier-detection; Gateway = edge ports/TLS; ServiceEntry = external hosts.
- Know the security pairing cold: PeerAuthentication controls mTLS mode (STRICT/PERMISSIVE/DISABLE), RequestAuthentication validates JWTs, and AuthorizationPolicy enforces allow/deny on identities and request attributes.
- When a route, policy or subset "does nothing", check visibility and matching before syntax: exportTo can hide a resource from the caller, a Gateway selector can match no workload, a short host resolves against the resource own namespace, and a subset whose labels match no pod has no endpoints. All four are valid YAML that quietly has no effect.
- Use istioctl analyze to catch misconfigurations and istioctl proxy-status to spot proxies that are STALE/out of sync with istiod - these are fast wins for the troubleshooting questions.
Study guide FAQ
What is the difference between sidecar mode and ambient mode?
Sidecar mode injects an Envoy proxy into every pod to handle both L4 and L7 traffic. Ambient mode is sidecar-less: a per-node ztunnel provides L4 connectivity and mTLS for all pods, and you add per-namespace/per-service-account waypoint proxies only where L7 features (advanced routing, L7 authorization) are needed, reducing per-pod overhead.
Why didn't my pods get a sidecar after I labeled the namespace?
Labeling a namespace with istio-injection=enabled only affects pods created after the label is applied, because injection happens through a mutating webhook at pod-creation time. Existing pods are not modified retroactively - you must restart or redeploy them (e.g. kubectl rollout restart) so the webhook injects the proxy.
How do I do a canary or weighted release in Istio?
Define version subsets (e.g. v1 and v2) in a DestinationRule using labels, then create a VirtualService with multiple http.route entries each carrying a weight (e.g. 95 to the v1 subset and 5 to the v2 subset, summing to 100). Gradually increase the v2 weight as it proves healthy, then shift 100% to v2.
What is the difference between PERMISSIVE and STRICT mTLS?
PERMISSIVE (the default) lets a workload accept both plaintext and mTLS traffic, which is essential during migration so non-injected or legacy clients keep working. STRICT accepts only mutually authenticated TLS, rejecting all plaintext. The common path is to run PERMISSIVE mesh-wide while onboarding, then tighten to STRICT once every client is in the mesh.
Official exam sources
The domain names and weightings on this page follow the published exam blueprint. Each source below records what it confirmed and when it was read, so the split can be checked rather than taken on trust.
- The Linux Foundation - Istio Certified Associate (ICA)link and content verified 8 September 2026Four domains totalling 100%: Installation, Upgrade & Configuration 20%, Traffic Management 35%, Securing Workloads 25%, Troubleshooting 20%. Matches the domain weights used by CertGrid practice exams and the figures recorded during the ICA remediation.