Skip to content
RU

no healthy upstream (Envoy, Istio): Why the Cluster Has No Endpoints

What it means: Envoy found no usable endpoint in the target cluster and answered 503 itself, without contacting your backend.

  1. List the cluster's endpoints in Envoy
  2. Check that pods are Ready and selected
  3. Compare Service, subset and pod labels
  4. Fix the health check path and port

Test your URL now

Requests the address and shows the status code and what the server sends back.

What "no healthy upstream" means

no healthy upstream is a local reply generated by Envoy's router filter, not by your application. When the load balancer cannot pick a host for the request, the router calls sendNoHealthyUpstreamResponse(), which sends the literal body no healthy upstream with status 503 by default and marks the request with the response flag UH (NoHealthyUpstream) in the access log (router.cc, access log response flags). You see it behind anything built on Envoy: Istio sidecars and gateways, Envoy Gateway, Contour, Emissary, Gloo and many API gateways.

The key point is that no connection to a backend was attempted. Envoy decided before connecting that the cluster had nothing to send to. That separates it from upstream connect error or disconnect/reset before headers (flag UF: a host was chosen, but the TCP or TLS connection to it failed) and from NR (no route matched, usually a 404). It is also not a timeout: that is upstream request timeout with a 504.

"Healthy" is narrower than it sounds. With default settings Envoy does not stop routing when every host fails its health checks: below the panic threshold (50% available hosts by default) it ignores health status and balances across all hosts (panic threshold). So on a default cluster the message usually means the cluster has zero hosts, not that its hosts are sick.

Common causes

CauseHow to recognise itFix
No Ready pods behind the Servicekubectl get endpointslices lists no ready addresses; pods are CrashLoopBackOff or fail their readiness probeFix the crash or the readiness probe; scale the Deployment above zero
Service selector matches no podsEndpoints empty although pods run; labels differ by a typo or after a renameMake spec.selector equal to the pod template labels
Istio subset matches no podsA DestinationRule subset (for example version: v2) has labels no pod carries; only requests routed to that subset failCorrect the subset labels or the VirtualService route
Panic mode disabledhealthy_panic_threshold set to 0% or fail_traffic_on_panic enabled, and every host fails health checksFix the health check or the backend; re-enable panic routing if all-or-nothing is not what you want
DNS-based cluster resolves to nothingA STRICT_DNS or LOGICAL_DNS cluster whose hostname does not resolve from the proxyFix the hostname or the resolver the proxy uses
Rollout or scale to zeroErrors only during deploys or at night when autoscaling removes the last replicaKeep a minimum replica count; use a PodDisruptionBudget and readiness gates

Outlier detection rarely empties a whole cluster by itself: by default it ejects at most 10% of hosts (max_ejection_percent), and an ejection lasts base_ejection_time, 30 seconds by default, multiplied by the number of previous ejections (outlier_detection.proto). If you raised that percentage to 100, a burst of 5xx can eject every host and produce this error until the ejections expire.

In Kubernetes and Istio

In a mesh, the cluster's endpoints come from the control plane, which builds them from Kubernetes EndpointSlices. A pod that is not Ready is not an endpoint, so a failing readiness probe removes the pod from Envoy's view before any Envoy health check runs. Start from Kubernetes, then look at what the proxy actually received:

kubectl get pods -l app=reviews -o wide
kubectl get endpointslices -l kubernetes.io/service-name=reviews -o wide
istioctl proxy-config endpoint deploy/productpage --cluster "outbound|9080|v2|reviews.default.svc.cluster.local"

An empty endpoint list for the exact cluster name in the error path means the subset or Service has no members. If the list has entries marked UNHEALTHY, look at health checks and outlier detection. In a sidecar you can also read Envoy's own view:

kubectl exec deploy/productpage -c istio-proxy -- pilot-agent request GET clusters | grep reviews

Each host line ends with its health status: healthy, or one or more of /failed_active_hc, /failed_eds_health and /failed_outlier_check (Envoy admin /clusters). No lines at all for the cluster is the zero-hosts case.

How to fix it

  1. Confirm the 503 comes from Envoy. curl -sv https://example.com/api/ 2>&1 | grep -iE '^< (HTTP|server)|no healthy'. A server: envoy header (unless your config rewrites it) and the exact body mean the proxy answered.
  2. Find the cluster. The access log line with flag UH carries the upstream cluster name if your format includes %UPSTREAM_CLUSTER%.
  3. Count the endpoints with the commands above. Zero endpoints: fix the selector, the subset labels or the crashing pods.
  4. Check readiness. kubectl describe pod POD | grep -A5 Readiness. A probe pointing at the wrong port or path keeps every pod out of rotation.
  5. Check Envoy health checks if you configured them: the path must answer 200, on the port and protocol the check uses. Run the same request from inside the cluster: kubectl exec deploy/productpage -c istio-proxy -- curl -s -o /dev/null -w '%{http_code}\n' http://reviews:9080/health.
  6. Look for ejections in the cluster stats: pilot-agent request GET stats | grep -E 'reviews.*outlier_detection.ejections_active'.

How to confirm the fix

istioctl proxy-config endpoint deploy/productpage | grep reviews
# 10.0.1.17:9080   HEALTHY   OK   outbound|9080|v2|reviews.default.svc.cluster.local
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/api/
# 200

The endpoint list must show at least one HEALTHY host for the cluster the route uses, and repeated requests must stop returning the 19-byte no healthy upstream body. To check the public URL from outside your network, run it through the HTTP status checker: it shows the status code and what the server sends back.

Related errors

60+ Status CodesComplete HTTP status code reference
Search by CodeQuick search for 200, 301, 404, 500
Categories 1xx–5xxInformational, success, redirects, errors
Examples & CausesWhen it occurs and how to fix it

Why teams trust us

60+
status codes
1xx-5xx
all categories
SEO
ranking impact
Free
online reference

How to use the reference

1

Enter code or keyword

2

Find description and causes

3

Apply recommendations

HTTP Status Codes Reference

HTTP status codes are three-digit numbers that the server returns for every request. They tell the browser and search crawlers what happened: success, redirect, or error.

Quick Search

Enter a code or keyword — instant result with description.

All Categories

1xx Informational, 2xx Success, 3xx Redirect, 4xx Client Error, 5xx Server Error.

Causes & Fixes

For each code: typical causes and concrete steps to resolve.

SEO Impact

How each code affects indexing and link juice transfer in Google.

Who uses this

Developers

choosing correct status

SEO

redirect optimization

DevOps

5xx error diagnostics

Marketers

landing page check

Common Mistakes

Confusing 301 and 302301 is permanent (passes SEO equity), 302 is temporary (does not).
Returning 200 on error"Soft 404s" — a 404 page with status 200 — confuse search engines and stay indexed.
Ignoring 5xx errors500, 503 are server failures. Prolonged episodes remove pages from the index.
Redirect chainsA→B→C instead of A→C slows loading and dilutes SEO equity. Simplify chains.

Best Practices

Use 301 for permanent redirectsThis passes 90–99% of link equity to the new URL.
Return 404 for missing pagesDon't mask 404 as 200 — it hurts SEO and UX.
Monitor 5xx in real timeSet up an HTTP monitor that alerts on 5xx.
Use 410 instead of 404410 Gone means "page permanently removed" — Google de-indexes faster.

Monitor HTTP statuses automatically

HTTP monitor alerts when response code changes or 5xx appears.

Sign up free

Learn more

Sources

Frequently Asked Questions

Is "no healthy upstream" an error in my application?

No. Envoy generates the body itself when it has no host to send the request to, so your application never sees the request. The cause is in endpoint discovery, labels, readiness or health checking.

Why do I get "no healthy upstream" when all my pods are running?

Running is not Ready. A pod that fails its readiness probe, or whose labels do not match the Service selector or the Istio subset, is not an endpoint, so Envoy sees an empty cluster.

Does a failing health check always cause "no healthy upstream"?

Not with default settings. When fewer than 50% of hosts are available Envoy enters panic mode and sends traffic to all hosts anyway, unless the panic threshold is 0% or fail_traffic_on_panic is enabled.

Why does it happen only during deployments?

Old pods leave the endpoint list before new ones become Ready, or a scale-to-zero removes the last replica. Keep a minimum replica count and make the rollout wait for readiness.

What is the UH flag in the Envoy access log?

UH stands for NoHealthyUpstream: there were no healthy upstream hosts in the cluster, and Envoy returned 503 itself.

Try the live tool that powered this guide

Free plan — 10 monitors, checks every 5 min, no card required. Upgrade for 1-minute interval and multi-region monitoring.