Multicluster peering
Verify multiple aspects of multicluster ambient mesh support and status.
Both before and after you link clusters into a multicluster mesh, you can use theistioctl multicluster check command, along with other observability checks, to verify multiple aspects of multicluster ambient mesh support and status.istioctl multicluster check
You can use the istioctl multicluster check --precheck command to check the individual readiness of each cluster before running istioctl multicluster link to link them in a multicluster mesh, and run it again after linking to confirm that the connections were successful. This command performs checks listed in the following sections, which you can review to understand what each check validates. Additionally, if any of the checks fail, run the command with the --verbose option, and review the following troubleshooting recommendations.
istioctl multicluster check --verbose --contexts="$context1,$context2"For more information about this command, see the CLI reference.
Starting in version 1.30, you can pass -o json or -o yaml to emit all check results grouped by cluster as a structured document. You can also run the command against an extracted bug-report directory to perform offline multicluster analysis without direct cluster access.
Incompatible environment variables
Checks whether the ENABLE_PEERING_DISCOVERY=true and optionally K8S_SELECT_WORKLOAD_ENTRIES=true environment variables are set incorrectly or are not supported for multicluster ambient mesh.
Example verbose output:
--- Incompatible Environment Variable Check ---
✅ Incompatible Environment Variable Check: K8S_SELECT_WORKLOAD_ENTRIES is valid ("")
✅ Incompatible Environment Variable Check: ENABLE_PEERING_DISCOVERY is valid ("true")
✅ Incompatible Environment Variable Check: all relevant environment variables are validIf this check fails, check your environment variables in your istiod configuration, such as by running helm get values --kube-context ${CLUSTER_CONTEXT} istiod -n istio-system -o yaml, and update your configuration.
License validity
Checks whether the license in use by istiod is valid for multicluster ambient mesh. Multicluster capabilities require an Enterprise level license for Solo Enterprise for Istio.
Example verbose output:
--- License Check ---
✅ License Check: license is valid for multiclusterIf your license does not support multicluster ambient mesh, contact your Solo account representative.
CNI DNS capture
Checks whether ambient DNS capture is enabled in the istio-cni-config ConfigMap. DNS capture is required for workloads to resolve global hostnames (.mesh.internal and custom Segment domains) used to route traffic to services across clusters. The check returns a warning if AMBIENT_DNS_CAPTURE is not set or is not true.
Example verbose output:
--- CNI DNS Capture Check ---
✅ CNI DNS Capture Check: AMBIENT_DNS_CAPTURE is enabledIf this check fails or warns, verify the value in the ConfigMap:
kubectl get configmap istio-cni-config -n istio-system \
-o jsonpath='{.data.AMBIENT_DNS_CAPTURE}'To enable DNS capture, set values.cni.ambient.dnsCapture=true in the istiod Helm chart values and upgrade your installation.
Pod health
Checks the health of the pods in the cluster. All istiod, ztunnel, and east-west gateway pods across the checked clusters must be healthy and running for the multicluster mesh to function correctly.
Example verbose output:
--- Pod Check (istiod) ---
NAME READY STATUS RESTARTS AGE
istiod-6d9cdf88cf-l47tf 1/1 Running 0 10m18s
✅ Pod Check (istiod): all pods healthy
--- Pod Check (ztunnel) ---
NAME READY STATUS RESTARTS AGE
ztunnel-dvlwk 1/1 Running 0 10m6s
✅ Pod Check (ztunnel): all pods healthy
--- Pod Check (eastwest gateway) ---
NAME READY STATUS RESTARTS AGE
istio-eastwest-857b77fc5d-qgnrl 1/1 Running 0 9m33s
✅ Pod Check (eastwest gateway): all pods healthyTo check any unhealthy pods, run the following commands. Consider checking the pod logs, and review Debug Istio.
kubectl get po -n istio-system
kubectl get po -n istio-eastwestEast-west gateway status
Checks the status of the east-west gateways in the cluster. When an east-west gateway is created, the gateway controller creates a Kubernetes service to expose the gateway. Once this service is correctly attached to the gateway and has an address assigned, the east-west gateway has a Programmed status of true.
Example verbose output:
--- Gateway Check ---
Gateway: istio-eastwest
Addresses:
- 172.18.7.110
Status: programmed ✅
✅ Gateway Check: all eastwest gateways programmedIf the Programmed status is not true, an issue might exist with the address allocation for the service. Check the east-west gateway with a command such as kubectl get svc -n istio-eastwest, and verify that your cloud provider can correctly allocate addresses to the service.
NodePort east-west gateway health
For east-west gateways configured for NodePort peering, checks that the gateway has not fallen back to reporting a ClusterIP address, and validates that the backing Service has a port named hbone.
Example verbose output:
--- NodePort Gateway Health Check ---
Gateway: istio-eastwest
Backing service has hbone port ✅
NodePort address detected ✅
✅ NodePort Gateway Health Check: all NodePort east-west gateways validIf this check fails, verify that the backing Service for the east-west gateway has a port named hbone and is correctly annotated for NodePort peering. You can also inspect the gloo.solo.io/NodePortConfigured status condition on the gateway: when True with reason Programmed, the HBONE port is correctly configured; when False with reason MissingHbonePort, the port is absent from the backing Service.
Remote peer gateway status
Checks the status of the remote peer gateways in the cluster, which represent the other peered clusters in the multicluster setup. These remote gateways configure the connection between the local cluster’s istiod control plane, and the peered clusters’ remote networks to enable xDS communication between peers. When the initial network connection between istiod and a remote peer is made, the gateway’s gloo.solo.io/PeerConnected status updates to true. Then, when the full xDS sync occurs between peers, the gateway’s gloo.solo.io/PeeringSucceeded status also updates to true. This check ensures that both statuses are true, and that the topology.istio.io/cluster label is set on the gateway.
Example verbose output:
--- Peers Check ---
Cluster: cluster2
Addresses:
- 172.18.7.130
Conditions:
- Accepted: True
- Programmed: True
- gloo.solo.io/PeerConnected: True
- gloo.solo.io/PeeringSucceeded: True
- gloo.solo.io/PeerDataPlaneProgrammed: True
Status: connected ✅
✅ Peers Check: all clusters connectedIf the connection is severed between the peers, the gloo.solo.io/PeerConnected status becomes false. A failed connection between peers can be due to either a misconfiguration in the peering setup, or a network issue blocking port 15008 on the remote cluster, which is the cross-network HBONE port that the east-west gateway listens on. Review the steps you took to link clusters together, such as the steps outlined in the Helm default network guide. Additionally, review any firewall rules or network policies that might block access through port 15008 on the remote cluster.
Remote gateway service account
Checks that the effective service account for each istio-remote Gateway resource matches the corresponding peer east-west gateway in the remote cluster. A mismatch can cause authentication failures when the local istiod control plane attempts to connect to the remote cluster.
Example verbose output:
--- Remote Gateway Service Account Check ---
Remote gateway istio-eastwest/istio-remote-peer-cluster2 service account: istio-eastwest/istio-eastwest ✅
✅ Remote Gateway Service Account Check: all remote gateway service accounts matchIf this check fails, review the service account configuration on your istio-remote Gateway resources and confirm they match the service account used by the east-west gateway in the corresponding remote cluster.
Intermediate certificate compatibility
Confirms the certificate compatibility between peered clusters. This check reads the root-cert.pem from the istio-ca-root-cert configmap in the istio-system namespace, and uses x509 certificate validation to confirm the root cert is compatible with all of the clusters’ ca-cert.pem intermediate certificate chains from the cacerts secret.
Example verbose output:
--- Intermediate Certs Compatibility Check ---
ℹ Intermediate Certs Compatibility Check: cluster cluster1 root certificate SHA256 sum: 6d18f32e134824c158d97f32618657c45d5a83839f838ada751757139481537e
ℹ Intermediate Certs Compatibility Check: cluster cluster2 root certificate SHA256 sum: 6d18f32e134824c158d97f32618657c45d5a83839f838ada751757139481537e
✅ Intermediate Certs Compatibility Check: cluster cluster1 has compatible intermediate certificates with cluster cluster2
✅ Intermediate Certs Compatibility Check: cluster cluster2 has compatible intermediate certificates with cluster cluster1
✅ Intermediate Certs Compatibility Check: all clusters have compatible intermediate certificatesIf this check fails because the root certs are not valid for each peered clusters’ intermediate certificate chain, you can check the istiod logs for TLS errors when attempting to communicate with a peered cluster, such as the following:
2025-12-04T22:09:22.474517Z warn deltaadsc disconnected, retrying in 24.735483751s: delta stream: rpc error: code = Unavailable desc = connection error: desc = "error reading server preface: remote error: tls: unknown certificate authority" target=peering-cluster2Ensure each cluster has a cacerts secret in the istio-system namespace. To regenerate invalid certificates for each cluster, follow the example steps in Create a shared root of trust.
Network configuration
Confirms the network configuration of the multicluster mesh. For multicluster peering setups that do not use a flat network topology, each cluster must occupy a unique network. The network name must be defined with the label topology.istio.io/network and set on both the istio-system namespace and the istio-eastwest gateway resource. The same network name must also be set as the NETWORK environment variable on the ztunnel daemonset. Each remote gateway that represents that cluster must have the topology.istio.io/network label equal to the network of the remote cluster.Each remote gateway must also have the topology.istio.io/cluster label set to the cluster ID of the remote cluster. This label is required for the peering controller to federate workload entries correctly. If a remote gateway is missing the topology.istio.io/cluster label, the check returns an error. If the label references a cluster ID not included in –contexts, the check returns an informational message.
Example verbose output:
--- Network Configuration Check ---
✅ Cluster cluster1 has network: cluster1
✅ Eastwest gateway istio-eastwest/istio-eastwest has correct network label: cluster1
✅ Cluster cluster2 has network: cluster2
✅ Eastwest gateway istio-eastwest/istio-eastwest has correct network label: cluster2
✅ Remote gateway istio-eastwest/istio-remote-peer-cluster2 references network cluster2 (clusters: [cluster2])
✅ Remote gateway istio-eastwest/istio-remote-peer-cluster1 references network cluster1 (clusters: [cluster1])
✅ Network Configuration Check: all network configurations are validMismatched network identities cause errors in cross-cluster communication, which leads to error logs in ztunnel pods that indicate a network timeout on the outbound communication. Notably, the destination address on these errors is a 240.X.X.X address, instead of the correct remote peer gateway address. You can run kubectl logs -l app=ztunnel -n istio-system --tail=10 --context ${CLUSTER_CONTEXT} | grep -iE "error|warn" to review logs such as the following:
2025-11-18T16:14:53.490573Z error access connection complete src.addr=240.0.2.27:46802 src.workload="ratings-v1-5dc79b6bcd-zm8v6" src.namespace="bookinfo" src.identity="spiffe://cluster.local/ns/bookinfo/sa/bookinfo-ratings" dst.addr=240.0.9.43:15008 dst.hbone_addr=240.0.9.43:9080 dst.service="productpage.bookinfo.mesh.internal" dst.workload="autogenflat.portfolio1-soloiopoc-cluster1.bookinfo.productpage-v1-54bb874995-hblwp.ee508601917c" dst.namespace="bookinfo" dst.identity="spiffe://cluster.local/ns/bookinfo/sa/bookinfo-productpage" direction="outbound" bytes_sent=0 bytes_recv=0 duration="10001ms" error="connection timed out, maybe a NetworkPolicy is blocking HBONE port 15008: deadline has elapsed"To troubleshoot these issues, be sure that you use unique network names to represent each cluster, and that you correctly labeled the cluster’s istio-system namespace with that network name, such as by running kubectl label namespace istio-system --context ${CLUSTER_CONTEXT} topology.istio.io/network=${CLUSTER_NAME}. You can also relabel the east-west gateway in the cluster, and the remote peer gateways in other clusters that represent this cluster.
ISTIO_META_NETWORK environment variable
Validates that all Istio proxy containers and sidecar-injector ConfigMap templates have ISTIO_META_NETWORK set, and that the value matches the topology.istio.io/network label on the istio-system namespace. A mismatch or missing value can cause network topology errors in multicluster routing.
Example verbose output:
--- ISTIO_META_NETWORK Check ---
✅ ISTIO_META_NETWORK Check: istiod proxy containers have ISTIO_META_NETWORK set to cluster1
✅ ISTIO_META_NETWORK Check: sidecar-injector ConfigMap template has ISTIO_META_NETWORK set to cluster1
✅ ISTIO_META_NETWORK Check: all ISTIO_META_NETWORK values match network labelIf this check fails, set ISTIO_META_NETWORK on your istiod deployment to match the topology.istio.io/network label on the istio-system namespace, and update the sidecar-injector ConfigMap template accordingly.
Stale workload entries
In flat network setups, checks for any outdated workload entries that must be removed from the multicluster mesh. Stale workload entries might exist from pods that were deleted, but the autogenerated entries for those workloads were not correctly cleaned up. If you do not use a flat network topology, no autogenerated workload entries exist to be validated, and this check can be ignored.
Example verbose output for a non-flat network setup:
--- Stale Workloads Check ---
⚠ Stale Workloads Check: no autogenflat workload entries foundIf you use a flat network topology, and this check fails with stale workload entries, run kubectl get workloadentries -n istio-system | grep autogenflat to list the autogenerated workload entries in the remote cluster, and compare the list to the output of kubectl get pods in the source cluster for those workloads. You can safely manually delete the stale workload entries in the remote cluster for pods that no longer exist in the source cluster, such as by running kubectl get workloadentries -n istio-system <entry_name>.
Missing local Segment resources
Checks for missing local Segment resources, which might cause auto-generated ServiceEntry resources that are received from a peered cluster to be silently dropped. If a remote cluster sends ServiceEntry resources for a Segment that does not exist locally, those entries are dropped without error. This check detects that condition and reports an actionable message for each rejected peer Segment.
Example verbose output:
--- Segment Readiness Check ---
✅ Segment Readiness Check: all peer Segments have matching local Segment resourcesIf this check reports missing Segment resources, create the corresponding Segment resource in the local cluster to match the one defined in the remote cluster.
Rejected peer Segment states
Checks for peer Segment resources that are in a rejected state due to hostname mismatches or overlapping Segment domains across clusters. Rejected segment states prevent the peering controller from federating workload entries for the affected services.
Example verbose output:
--- Peer Segment State Check ---
✅ Peer Segment State Check: no rejected peer Segments detectedIf this check fails, review the Segment resources in each cluster for conflicting spec.domain values or mismatched hostnames, and ensure that Segment domains are unique across the multicluster mesh.
Hostname conflicts
Checks for hostname conflicts in auto-generated ServiceEntry resources by reading conflict annotations added by the peering controller. Hostname conflicts occur when two services in different clusters or namespaces resolve to the same hostname in the mesh, causing one ServiceEntry to override the other.
Example verbose output:
--- Hostname Conflict Check ---
✅ Hostname Conflict Check: no hostname conflicts detected in auto-generated ServiceEntry resourcesIf this check reports conflicts, review the conflicting services and adjust your Segment configuration to ensure each service resolves to a unique hostname across the mesh.
Metrics
You can also check metrics that are built into the Solo distribution of Istio to verify multiple aspects of the multicluster peering status.
Each peering metric has the labels source and peer, which appear as fields in the metric. The source is the local istiod instance in the cluster where the metric is emitted, and peer is the peered remote cluster. The convergence time metrics are important for understanding how quickly configuration propagates to peer clusters. A high convergence time can indicate slow propagation, connectivity issues, or that the peer or network is under load.
Port-forward to the istiod pod in each cluster to access its metrics endpoint.
kubectl port-forward -n istio-system deploy/istiod 15014:15014 --context ${context1}In a separate terminal, query the metrics endpoint and filter for peer metrics.
curl -s http://localhost:15014/metrics | grep '^peer_'Repeat for each cluster in the mesh, updating the
--contextflag.
The following peer metrics are available.
| Metric | Description |
|---|---|
peer_connection_state | The connection state of peered remote clusters (1 = connected, 0 = disconnected). |
peer_convergence_time_bucket | The cumulative count of convergence times, which measures the delay between sending an xDS request to a peer cluster and receiving an ACK or NACK. This metric is captured in seconds for the following intervals (buckets): 0.01, 0.1, 0.5, 1, 3, 5, 10, 20, 30. |
peer_convergence_time_count | The total number of xDS requests to peer clusters for which an ACK or NACK was received since istiod was last started. |
peer_convergence_time_sum | The sum of all convergence times in seconds since istiod was last started. |
peer_xds_config_size_bytes_bucket | The distribution of xDS configuration sizes received from peer clusters. |
peer_xds_config_size_bytes_count | The number of xDS configurations received from peer clusters. |
peer_xds_config_size_bytes_sum | The sum of all xDS configuration sizes received from peer clusters since the last start of the Istio proxy. |
If you use Grafana to monitor Istio performance, you can also check out the Grafana dashboards in the Solo Communities of Practice (COP) repository. For example, you can use the istio-peering-dashboard to monitor and verify peering connection between clusters, and the istio-global-services-dashboard to monitor locality-aware traffic distribution and endpoint health across clusters, networks, zones, and regions.
Common issues
Cross-cluster traffic fails with 503 no healthy upstream when using DestinationRule subsets
In multicluster ambient meshes, cross-cluster requests that use DestinationRule subset selectors fail with 503 no_healthy_upstream when the subset labels exist on local pods but not on the autogenerated WorkloadEntry resources in remote clusters. By default, the peering controller propagates only a minimal set of labels (app, solo.io/*, security.istio.io/tlsMode) to remote WorkloadEntry resources, so custom subset labels such as version, rollouts-pod-template-hash, or canary labels are not present in remote clusters.
To resolve this, set the ENABLE_PEERING_LABEL_PROPAGATION environment variable on the istiod deployment.
kubectl set env deployment/istiod -n istio-system \
ENABLE_PEERING_LABEL_PROPAGATION=allThe following values are supported:
| Value | Behavior |
|---|---|
all | Propagate all labels from the source pod or service. |
| Comma-delimited label keys | Propagate only the specified labels. Reduces data over the wire. For example: app,version,canary. |
In flat-network peering environments, pod-level labels are propagated to per-pod WorkloadEntry resources. In non-flat environments, service-level labels are propagated to the aggregate WorkloadEntry per service.
Remote peering ServiceEntry not created for services whose pod selector matches waypoint pods (1.30.0–1.30.1)
In 1.30.0 and 1.30.1, the peering controller does not create a remote ServiceEntry for services whose pod selector matches waypoint proxy pods. Affected services with global or segment scope are invisible in remote clusters.
To resolve this, upgrade to 1.30.2 or later.