Quick Answer
Gateway API separates infrastructure ownership from route ownership through resources such as GatewayClass, Gateway, and HTTPRoute. Platform teams can own listener and infrastructure policy, while application teams attach routes within allowed namespaces and hostnames.
Detailed Answer
Think of a shopping mall. The mall operator controls entrances, fire doors, and security rules, while each store controls its own sign and product layout. Traditional Ingress often turns the mall operator into the person editing every store sign. Gateway API gives the building owner and store owner separate but connected responsibilities. Gateway API was designed to make Kubernetes traffic management more expressive and role-oriented than the original Ingress API. Ingress is useful but often overloaded with controller-specific annotations, which blur ownership and make portability harder. Gateway API introduces clearer resources for infrastructure providers, cluster operators, and application teams so each group can manage the layer it actually owns. The flow starts with GatewayClass, which identifies the implementation or controller. A Gateway represents a deployed data-plane entry point with listeners such as HTTPS on port 443. HTTPRoute, GRPCRoute, or other route resources define application-level routing rules and attach to a Gateway when allowed by namespace and listener policy. The controller reconciles those resources into load balancer, proxy, or service mesh configuration. In production, Gateway API helps with multi-team environments. Platform engineers can standardize TLS, listener ports, allowed namespaces, and shared infrastructure. App teams can publish or update service routes without editing a central Ingress file. Operators should monitor route attachment status, accepted conditions, listener conflicts, certificate readiness, and controller reconciliation errors. This shifts troubleshooting from annotation archaeology to explicit status fields. The gotcha is that Gateway API does not magically remove governance. If allowedRoutes is too permissive, one team can still attach unexpected hostnames or paths. If it is too strict, teams see routes that never attach and traffic silently stays on the old path. Architects need namespace policy, hostname ownership, certificate automation, and clear dashboards showing which routes are accepted by which Gateway.
Code Example
# Install Gateway API CRDs for clusters that do not include them yet
kubectl apply -f https://github.com/kubernetes-sigs/gateway-api/releases/download/v1.3.0/standard-install.yaml
# Apply the shared production Gateway owned by the platform team
kubectl apply -f platform-gateway.yaml
# Apply an application route owned by the payments team
kubectl apply -f payments-route.yaml
# Verify whether the route was accepted by the Gateway controller
kubectl get httproute payments-api -n payments -o jsonpath='{.status.parents[*].conditions[*].type}'
# platform-gateway.yaml
apiVersion: gateway.networking.k8s.io/v1 # Uses the stable Gateway API
kind: Gateway # Declares a shared traffic entry point
metadata:
name: public-web # Platform-owned public web Gateway
namespace: platform-ingress # Keeps infrastructure config in a platform namespace
spec:
gatewayClassName: prod-nginx # Selects the installed Gateway controller implementation
listeners:
- name: https # Listener name used by routes and status
protocol: HTTPS # Accepts encrypted HTTP traffic
port: 443 # Standard public HTTPS port
hostname: '*.interviewcatalog.example' # Restricts accepted hostnames to the platform domain
allowedRoutes:
namespaces:
from: Selector # Allows only selected namespaces to attach routes
selector:
matchLabels:
expose-public: 'true' # Namespace opt-in controlled by platform policy
---
apiVersion: gateway.networking.k8s.io/v1 # Uses the stable route API
kind: HTTPRoute # Defines app-owned HTTP routing rules
metadata:
name: payments-api # Route for the payments service
namespace: payments # Owned by the payments application team
spec:
parentRefs:
- name: public-web # Attaches to the platform Gateway
namespace: platform-ingress # References the Gateway namespace explicitly
hostnames:
- payments.interviewcatalog.example # Hostname this team is allowed to serve
rules:
- backendRefs:
- name: payments-api # Kubernetes Service receiving traffic
port: 8080 # Service port for the API backend◈ Architecture Diagram
┌──────────┐
│GatewayCls│
└────┬─────┘
↓
┌──────────┐
│ Gateway │
└────┬─────┘
↓
┌──────────┐
│HTTPRoute │
└────┬─────┘
↓
┌──────────┐
│ Service │
└──────────┘Quick Answer
Check the Ingress resource rules (host, path, backend service/port), verify the controller is running and has processed the Ingress, confirm the backend Service has healthy endpoints, validate TLS secrets exist, and inspect controller logs for routing errors. Work from outside in: DNS, load balancer, Ingress controller, Service, Endpoints, Pods.
Detailed Answer
Think of a mall directory kiosk. Visitors look up the store name (host), follow the floor and section (path), and expect to reach the store (backend). If the directory has a typo, the store moved, the hallway is blocked, or the store is closed, visitors cannot get there. Ingress troubleshooting follows the same logic: verify every link in the chain from the external request to the running pod. An Ingress resource is a routing declaration, not a router itself. It tells an Ingress controller (NGINX, ALB, Traefik, Istio Gateway) how to route external traffic based on hostname and URL path. The controller watches Ingress objects, updates its routing table (nginx.conf, ALB rules, envoy routes), and directs traffic to the backend Service and port specified. If any part of this chain is misconfigured, traffic either returns 404, 503, or times out with no obvious error. The troubleshooting sequence starts at the DNS and load balancer layer. Verify that the domain resolves to the correct IP or ALB hostname. Check whether the load balancer health checks pass for the Ingress controller pods. Then inspect the Ingress resource: does the host field match the exact domain being requested? Does the path match the URL pattern (prefix vs exact)? Is the backend service name and port correct? A common mistake is specifying the container port instead of the Service port, or using a service name that does not exist in the same namespace. Next, check the Ingress controller itself. Is the controller pod running and ready? Has it synced the Ingress resource (describe the Ingress and look for events or Address field population)? Check controller logs for configuration reload errors, upstream connection failures, or TLS certificate problems. For NGINX Ingress Controller, the logs show every routing decision and upstream selection. An empty Address field on the Ingress means the controller has not processed it, often because the ingressClassName does not match or the controller is filtering by namespace. Behind the Ingress, the Service must have healthy endpoints. Run kubectl get endpoints to confirm the Service has pod IPs listed. Empty endpoints mean either no pods match the Service selector, pods exist but fail readiness probes, or the selector labels are mismatched. Even with endpoints populated, the target port must match what the container listens on. A Service targeting port 8080 when the app listens on 3000 will show connection refused in controller logs. The non-obvious gotcha is TLS misconfiguration. If the Ingress specifies a TLS section but the Secret does not exist, contains an expired certificate, or has a subject name that does not match the host field, some controllers serve a default fake certificate while others reject the connection entirely. Another common issue is path precedence: if you have both /api and /api/v2 paths, the ordering and pathType (Prefix vs Exact vs ImplementationSpecific) determine which rule matches. Some controllers require a trailing slash; others do not.
Code Example
# Inspect the Ingress resource for host, path, backend, and TLS configuration
kubectl describe ingress payments-api -n payments
# Check if the Ingress has an Address assigned (empty = controller has not processed it)
kubectl get ingress payments-api -n payments
# Verify the backend Service exists and has the correct port
kubectl get svc payments-api -n payments
# Check if the Service has endpoints (empty = no ready pods match the selector)
kubectl get endpoints payments-api -n payments
# Verify pod readiness — unready pods are excluded from endpoints
kubectl get pods -n payments -l app=payments-api -o wide
# Check the Ingress controller logs for routing errors or upstream failures
kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginx --tail=100 | grep payments
# Verify the TLS secret exists and is not expired
kubectl get secret payments-tls -n payments
kubectl get secret payments-tls -n payments -o jsonpath='{.data.tls\.crt}' | base64 -d | openssl x509 -noout -dates
# Test connectivity from inside the cluster to the Service directly (bypass Ingress)
kubectl exec -n payments payments-api-7f8d9c-x4k -- curl -s http://payments-api.payments.svc:8080/health◈ Architecture Diagram
┌──────────┐
│ Client │
└────┬─────┘
↓ DNS
┌──────────┐
│ LB │
└────┬─────┘
↓ health check
┌──────────────────┐
│ Ingress Controller│
│ (nginx/alb) │
└────┬─────────────┘
↓ host + path match
┌──────────┐
│ Service │
└────┬─────┘
↓ endpoints
┌──────────┐
│ Pods │
│ (ready?) │
└──────────┘