When an Ingress returns 502 or 503, the ingress controller is alive and answering you. What it cannot do is get a good response from a backend pod. The two codes point in slightly different directions:
- 503 usually means the controller has no healthy endpoint for that Service, so it has nothing to send traffic to. With ingress-nginx the page reads "503 Service Temporarily Unavailable".
- 502 usually means the controller found an endpoint but the connection failed or returned an invalid response: wrong port, wrong protocol, a pod that crashed, or a connection reset.
- 404 from the controller's default backend means no Ingress rule matched the host or path, which is a different problem.
See the general references for 502, 503 and the 502 Bad Gateway guide. The steps below are specific to Kubernetes.
Check in this order
- Does the Service have endpoints? (The single most useful check for 503.)
- Are the pods Ready?
- Do Service port, targetPort and the container port agree?
- Does the Ingress point at the right Service, port and namespace?
- Is the backend protocol correct (HTTP, HTTPS, gRPC)?
- What do the controller logs say?
- Is something in front of the controller (cloud load balancer, CDN) causing it?
1. Check endpoints
kubectl get ingress -n <ns>
kubectl describe ingress <name> -n <ns>
kubectl get svc,endpoints -n <ns>
kubectl get endpointslices -n <ns> -l kubernetes.io/service-name=<svc>
If the ENDPOINTS column shows <none>, the controller has no backend, and you get 503. Two reasons dominate: the Service selector does not match the pod labels, or the pods are not Ready.
kubectl get svc <svc> -n <ns> -o jsonpath='{.spec.selector}'
kubectl get pods -n <ns> --show-labels
A one-letter mismatch between selector and pod labels leaves the Service empty. The Ingress describe output also warns about a missing Service: <error: endpoints "x" not found>.
2. Pods must be Ready
Only pods that pass their readiness probe become endpoints. kubectl get pods should show 1/1 under READY. If not, kubectl describe pod shows probe failures; for restarting pods go to CrashLoopBackOff. During a rolling deployment with a too-aggressive maxUnavailable or no readiness probe, you can briefly have no ready pods, which shows up as short bursts of 503.
3. Ports must line up
apiVersion: v1
kind: Service
spec:
selector: { app: web }
ports:
- port: 80 # what the Ingress references
targetPort: 8080 # what the container listens on
The app must listen on 0.0.0.0:8080, not only 127.0.0.1, or the connection fails and you see 502. Test from inside the cluster:
kubectl run curl --rm -it --image=curlimages/curl --restart=Never -- \
curl -sv http://<svc>.<ns>.svc.cluster.local:80/
kubectl port-forward pod/<pod> 8080:8080 # then curl localhost:8080
If the Service works from inside the cluster, the app and Service are fine, and the problem sits in the Ingress or in front of it.
4. Ingress rules
Check ingressClassName, host, path and the Service reference. The Service must be in the same namespace as the Ingress, and the port in backend.service.port must be the Service's port:
spec:
ingressClassName: nginx
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: web
port: { number: 80 }
5. Backend protocol
If your pod serves HTTPS but the controller speaks plain HTTP to it, you get 502 (and often an SSL error in the logs). With the ingress-nginx controller, set the annotation:
metadata:
annotations:
nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" # or GRPC
Annotation names differ between controllers (Traefik, HAProxy, cloud-provider ingress), so check your controller's documentation.
6. Controller logs
kubectl logs -n ingress-nginx deploy/ingress-nginx-controller --tail=100
Lines worth knowing in ingress-nginx logs:
upstream prematurely closed connection while reading response header from upstream
connect() failed (111: Connection refused) while connecting to upstream
upstream timed out (110: Operation timed out) while reading response header from upstream
no live upstreams while connecting to upstream
"Connection refused" means the pod port is wrong or the app is down; see nginx connect() failed (111). "Timed out" is a slow backend (that case normally yields 504); see nginx upstream timed out. "Prematurely closed" suggests the app crashed or closed keep-alive connections earlier than the proxy expected. "No live upstreams" is the 503 case from step 1.
7. Layers in front
A cloud load balancer in front of the controller has its own health checks. If it marks the controller nodes unhealthy, you get 502 or 503 from the cloud edge with a different page style. Identify who answered with the response headers:
curl -sI https://app.example.com/ | head -n 15
Compare against the HTTP Header Checker and CDN Detector results. A Server: nginx page is typically the controller itself, while a CDN or cloud load balancer sets its own headers. Large uploads or headers can also trigger errors; see nginx 413.
Verify
Reload the URL, then run the HTTP Status Checker and the Website Health Check from outside the network. Keep an uptime monitor on the hostname, using a health endpoint that exercises the backend.
Next steps
- Read the 502 Bad Gateway guide and 503 guide.
- Tune proxy timeouts using nginx 502 and 504 tuning.
- Fix restarting pods with CrashLoopBackOff.
- Review load balancer health checks.
FAQ
Why do I get 503 only during deployments?
Old pods are removed before new ones are Ready. Add readiness probes, a preStop delay, and set the rolling update strategy so capacity never drops to zero.
502 or 503, which is worse?
Neither is inherently worse. 503 usually points at missing endpoints, 502 at a failing connection to an existing one.
The Service works in-cluster but not through the Ingress.
Check ingressClassName, the host rule, the backend port and protocol annotation, and the controller logs.
Does the Ingress need a specific controller?
Yes. An Ingress resource does nothing without a controller that implements its class.
Get notified when this fix updates
Plus occasional practical tips on uptime and monitoring, nothing else. Unsubscribe anytime.
This page is general information written by our editorial team and updated when documentation changes. Servers and providers differ, so test changes safely first. See our editorial policy and disclaimer.