Introduction
Kubernetes Ingress routes external HTTP/S traffic to Services within the cluster. When a client requests a path mapped by the Ingress, the Ingress controller forwards the request to the Service, which load balances across its ready endpoint Pods. This chain—Ingress to Service to Pod—requires each link to function correctly.
When an Ingress returns HTTP 503 Service Unavailable, the Ingress controller itself is typically healthy; the problem lies downstream. The Service has no ready endpoints to receive traffic. This can happen even when Pods are running and the Service exists. The root cause is usually a mismatch between Service selectors and Pod labels, Pods failing readiness probes, or namespace misconfiguration.
Understanding how Services select Pods through label selectors, how readiness gates endpoint inclusion, and how Ingress references Services is essential for debugging 503 errors in Kubernetes Ingress deployments.
Symptoms
When Kubernetes Ingress returns 503 due to missing endpoints, you will observe these symptoms:
- HTTP requests to Ingress paths return 503 Service Unavailable
- Ingress controller logs show "no healthy upstream" or similar
kubectl get endpointsshows empty addresses for the Service- Pods are running but not listed as Service endpoints
- Recent rollout, label change, or probe modification preceded the outage
- Some paths work while others fail (different Services)
- Nginx/HAProxy Ingress controller logs show "no live upstreams"
Common error responses:
``` HTTP/1.1 503 Service Unavailable Content-Type: text/html
<html> <head><title>503 Service Temporarily Unavailable</title></head> <body> <center><h1>503 Service Temporarily Unavailable</h1></center> <hr><center>nginx</center> </body> </html> ```
Nginx Ingress controller logs:
2026/01/15 10:30:45 [error] 1234#1234: *5678 upstream temporarily disabled while connecting to upstream
2026/01/15 10:30:45 [warn] 1234#1234: *5678 no live upstreams while connecting to upstreamkubectl endpoints showing empty:
kubectl get endpoints my-service
NAME ENDPOINTS
my-service <none>Common Causes
Several factors cause Kubernetes Services to have no endpoints:
- 1.Service selector doesn't match Pod labels: The Service's label selector doesn't match any Pod labels. This commonly happens after Deployment label changes without updating the Service.
- 2.Pods not in Ready state: Pods may be running but not Ready due to failing readiness probes. Non-Ready Pods are excluded from Service endpoints.
- 3.Namespace mismatch: Ingress references a Service in the wrong namespace, or Pods and Service are in different namespaces.
- 4.Service name typo in Ingress: The Ingress backend references a Service name that doesn't exist or has a typo.
- 5.Port mismatch: Ingress backend port doesn't match Service port, causing connection failures.
- 6.Pod selector empty: Service was created without selector (valid for external Services), but no Endpoints were manually created.
- 7.All Pods terminated: Deployment scaled to 0 replicas or all Pods are in failed/completed state.
- 8.Readiness probe too strict: Readiness probe conditions are too aggressive, preventing any Pod from becoming Ready.
Step-by-Step Fix
Follow these steps to diagnose and resolve Ingress 503 endpoint issues:
Step 1: Check Service endpoints
Verify the Service has endpoints:
```bash # Check endpoints for the Service kubectl get endpoints my-service -n my-namespace
# Detailed endpoint information kubectl describe endpoints my-service -n my-namespace
# Check via endpointslices (newer Kubernetes) kubectl get endpointslices -l kubernetes.io/service-name=my-service -n my-namespace
# Expected output with endpoints: NAME ENDPOINTS AGE my-service 10.244.0.5:8080,10.244.0.6:8080 5m
# Empty endpoints indicates the problem: NAME ENDPOINTS AGE my-service <none> 5m ```
Step 2: Compare Service selector with Pod labels
Check for selector/label mismatch:
```bash # Get Service selector kubectl get service my-service -n my-namespace -o jsonpath='{.spec.selector}' # Output: {"app":"myapp","environment":"production"}
# Get Pod labels kubectl get pods -n my-namespace --show-labels
# Output: NAME READY STATUS LABELS myapp-abc1 1/1 Running app=myapp,environment=staging,pod-template-hash=abc1 myapp-abc2 1/1 Running app=myapp,environment=staging,pod-template-hash=abc2
# Mismatch: Service wants environment=production, Pods have environment=staging ```
Step 3: Check Pod readiness status
Verify Pods are Ready:
```bash # List Pods with readiness status kubectl get pods -n my-namespace -o wide
# Pods not Ready (0/1 or similar): NAME READY STATUS RESTARTS myapp-abc1 0/1 Running 0
# Describe Pod for probe status kubectl describe pod myapp-abc1 -n my-namespace
# Look for Readiness probe events: Events: Warning Unhealthy 30s kubelet Readiness probe failed: HTTP probe failed with statuscode: 500 ```
Step 4: Check Deployment and Service alignment
Verify Deployment labels match Service selector:
```bash # Get Deployment labels kubectl get deployment myapp -n my-namespace -o jsonpath='{.spec.template.metadata.labels}'
# Get Service selector kubectl get service my-service -n my-namespace -o jsonpath='{.spec.selector}'
# They must match for Pods to be selected ```
Step 5: Fix selector/label mismatch
Update Service selector or Pod labels:
```bash # Option 1: Update Service selector to match Pod labels kubectl patch service my-service -n my-namespace -p '{"spec":{"selector":{"app":"myapp","environment":"staging"}}}'
# Option 2: Update Deployment labels (requires rollout) kubectl set env deployment/myapp ENVIRONMENT=production -n my-namespace # Or edit Deployment kubectl edit deployment myapp -n my-namespace # Update spec.template.metadata.labels
# After Deployment update, verify new Pods have correct labels kubectl get pods -n my-namespace --show-labels ```
Step 6: Fix readiness probe issues
If Pods aren't becoming Ready:
```bash # Check probe configuration kubectl get deployment myapp -n my-namespace -o yaml | grep -A 20 readinessProbe
# Example failing probe: readinessProbe: httpGet: path: /health port: 8080 initialDelaySeconds: 5 periodSeconds: 10
# Test the health endpoint manually kubectl exec -it myapp-abc1 -n my-namespace -- curl -f http://localhost:8080/health
# If endpoint returns errors, fix application or adjust probe: kubectl patch deployment myapp -n my-namespace --type='json' -p='[{"op": "replace", "path": "/spec/template/spec/containers/0/readinessProbe/httpGet/path", "value":"/api/health"}]' ```
Step 7: Verify Ingress references correct Service
Check Ingress backend configuration:
```bash # Get Ingress configuration kubectl get ingress my-ingress -n my-namespace -o yaml
# Check backend specification: spec: rules: - host: example.com http: paths: - path: /api backend: service: name: my-service # Must exist port: number: 80 # Must match Service port
# Verify Service exists and port matches kubectl get service my-service -n my-namespace ```
Step 8: Check namespace consistency
Verify all resources are in expected namespaces:
```bash # Check Ingress namespace kubectl get ingress --all-namespaces | grep my-ingress
# Check Service namespace kubectl get service my-service --all-namespaces
# Check Pod namespace kubectl get pods -l app=myapp --all-namespaces
# Ingress can only reference Services in same namespace # For cross-namespace, use ExternalName Service ```
Step 9: Verify endpoint recovery
After fixes, confirm endpoints are populated:
```bash # Check endpoints are now populated kubectl get endpoints my-service -n my-namespace
# Expected output: NAME ENDPOINTS AGE my-service 10.244.0.5:8080,10.244.0.6:8080 10m
# Test Ingress curl -H "Host: example.com" http://ingress-ip/api
# Should return 200 instead of 503 ```
Verification
After fixing the issue, verify the complete chain:
```bash # Verify Pods are Ready kubectl get pods -n my-namespace # All should show 1/1 or n/n Ready
# Verify Service has endpoints kubectl get endpoints my-service -n my-namespace # Should show IP:port addresses
# Verify Ingress routes correctly curl -v http://example.com/api # Should return 200 OK
# Check Ingress controller logs are clean kubectl logs -n ingress-nginx deployment/ingress-nginx-controller | grep -i error ```
Complete verification script:
```bash #!/bin/bash NS="my-namespace" SVC="my-service"
echo "Checking Service endpoints..." EP=$(kubectl get endpoints $SVC -n $NS -o jsonpath='{.subsets[0].addresses}') if [ -z "$EP" ]; then echo "FAIL: No endpoints for Service $SVC" exit 1 else echo "OK: Service has endpoints" fi
echo "Testing Ingress..." HTTP=$(curl -s -o /dev/null -w "%{http_code}" http://example.com/api) if [ "$HTTP" == "200" ]; then echo "OK: Ingress returns 200" else echo "FAIL: Ingress returns $HTTP" exit 1 fi ```
Prevention
To prevent Kubernetes Ingress 503 endpoint issues:
- 1.Keep Deployment labels and Service selectors synchronized: Treat them as a unit.
```yaml # Deployment spec: template: metadata: labels: app: myapp version: v1
# Service (selector must match) spec: selector: app: myapp version: v1 ```
- 1.Monitor endpoint counts: Set up alerts for Services with zero endpoints.
# Prometheus alert
- alert: ServiceNoEndpoints
expr: kube_endpoint_address_available == 0
for: 5m
labels:
severity: critical- 1.Use meaningful readiness probes: Probes should verify actual request-handling capability.
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 10
periodSeconds: 5
failureThreshold: 3- 1.Validate before applying changes: Use dry-run to check impact.
kubectl apply -f service.yaml --dry-run=client- 1.Use GitOps for coordination: Manage Deployment and Service together in same commit.
- 2.Document Service dependencies: Document which Services are critical for which Ingress paths.
## Service Dependencies
- /api -> api-service (requires 2+ endpoints)
- /web -> web-service (requires 1+ endpoints)- 1.Implement health checks for Services: Periodically verify Service endpoints exist.
# CronJob to check endpoints
kubectl get endpoints -A -o json | jq '.items[] | select(.subsets | length == 0) | .metadata.name'Related Articles
- [Fix Envoy Rate Limit Configuration with envoyproxy/ratelimit](envoyproxy-ratelimit-configuration-guide)
- [Fix Fix Argocd App Not Syncing Issue in Kubernetes](fix-argocd-app-not-syncing)
- [Fix Fix Argocd Sync Conflict Issue in Kubernetes](fix-argocd-sync-conflict)
- [Fix ArgoCD Sync Timeout](fix-argocd-sync-timeout)
- [How to Fix Cilium Identity Exhaustion and Endpoint Allocation Failed](fix-cilium-identity-exhaustion)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "Kubernetes Ingress Returning 503 Because the Backend Service Has No Endpoints", "description": "Resolve Kubernetes Ingress 503 Service Unavailable responses by checking Service selectors, Pod readiness, and whether the backend actually exposes ready endpoints.", "url": "https://www.fixwikihub.com/kubernetes-ingress-503-service-backend-no-endpoints", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2026-01-22T21:09:23.304Z", "dateModified": "2026-01-22T21:09:23.304Z" } </script>