Complete Request Flow: From Browser to Kubernetes Pod — Every Hop Explained

You type a URL into a browser and press Enter. Half a second later, a response renders on screen. Between those two events, a request passes through more distinct systems than most developers have ever mapped end to end — DNS resolvers, TLS handshakes, cloud load balancers, Kubernetes controllers, kernel netfilter rules, and container network interfaces — before the first byte of your application code runs.

This post maps every hop. Not at a hand-wavy architecture level, but precisely: what component handles the packet, what decision it makes, what it reads or writes, and what happens next. By the end, you will have a mental model accurate enough to diagnose failures at any layer.


The Complete Picture

Browser
  │
  │  1. DNS resolution
  ▼
DNS Resolver (OS → Recursive → Authoritative)
  │
  │  2. TCP connection + TLS handshake
  ▼
Cloud Load Balancer  (AWS ALB / GCP GLB / Azure Front Door)
  │
  │  3. HTTP/2 or HTTP/1.1 request forwarded
  ▼
Ingress Controller Pod  (nginx / Traefik / Envoy)
  │
  │  4. Path/host routing → selects backend Service
  ▼
kube-proxy (iptables / IPVS rules on the node)
  │
  │  5. DNAT: Service ClusterIP → Pod IP
  ▼
CNI Network Plugin  (Calico / Flannel / Cilium)
  │
  │  6. Packet routed through overlay or underlay to target node
  ▼
Target Node Network Stack
  │
  │  7. veth pair → container network namespace
  ▼
Application Pod
  │
  │  8. Application code handles request
  ▼
Response travels back through the same layers

Each arrow is a real system boundary with latency, failure modes, and configuration. Let us walk through each one.


Hop 1: DNS Resolution

The browser has a hostname — api.example.com. It needs an IP address before it can open a TCP connection.

The Resolution Chain

Browser cache
  │ miss
  ▼
OS DNS cache  (nscd / systemd-resolved / dscacheutil)
  │ miss
  ▼
OS stub resolver  → reads /etc/resolv.conf
  │
  ▼
Recursive resolver  (ISP / 8.8.8.8 / 1.1.1.1)
  │ cache miss
  ├──► Root nameserver  (.  →  delegates to .com)
  ├──► TLD nameserver  (.com  →  delegates to example.com)
  └──► Authoritative nameserver  (example.com  →  returns A/CNAME record)
  │
  ▼
Returns: api.example.com → 203.0.113.42  (TTL: 60s)

For Kubernetes-hosted services, the authoritative nameserver is usually a cloud DNS product (Route 53, Cloud DNS, Azure DNS). The record it returns is the IP of the cloud load balancer sitting in front of your cluster — not a pod IP, and not a cluster IP.

DNS TTL and Caching

The TTL on the DNS record determines how long resolvers cache the answer. For cloud load balancers, 60 seconds is common. For latency-sensitive failover, lower TTLs (15–30 seconds) are used, at the cost of more DNS queries.

Browsers have their own DNS caches with independent TTLs. Chrome, for example, caps its DNS cache TTL at 60 seconds regardless of the record’s TTL. This means even with a 0-second TTL in DNS, browsers will cache the result for up to a minute — an important detail during IP migration.

Latency

  • Browser/OS cache hit: < 1 ms
  • Recursive resolver cache hit: 5–20 ms (round-trip to resolver)
  • Full recursive resolution (root → TLD → authoritative): 50–200 ms

Hop 2: TCP Connection and TLS Handshake

With an IP address in hand, the browser opens a TCP connection to port 443.

TCP Three-Way Handshake

Browser                          Load Balancer
  │                                    │
  │── SYN ──────────────────────────►  │
  │                                    │
  │  ◄──────────────────────── SYN-ACK │
  │                                    │
  │── ACK ──────────────────────────►  │
  │                                    │
  │  [TCP connection established]      │

One round-trip time (RTT) for TCP establishment. With geographic co-location, this is 5–20ms. Cross-continental: 80–200ms.

TCP Fast Open (TFO) allows data to be sent on the SYN packet for repeat connections, eliminating this round-trip for known servers — but browser support and server-side enablement is inconsistent.

TLS 1.3 Handshake

Modern TLS 1.3 completes in one round trip (1-RTT) after TCP:

Browser                          Load Balancer
  │                                    │
  │── ClientHello ──────────────────►  │
  │   (supported ciphers, key share,   │
  │    SNI: api.example.com)           │
  │                                    │
  │  ◄───────────── ServerHello ───── │
  │  ◄──────── EncryptedExtensions ── │
  │  ◄──────────────── Certificate ── │
  │  ◄──────────── CertificateVerify  │
  │  ◄──────────────────── Finished ──│
  │                                    │
  │── Finished + HTTP Request ──────►  │
  │  [Encrypted channel established]   │

0-RTT resumption (TLS 1.3 session tickets) allows the browser to send the HTTP request alongside the first TLS message for resumed sessions — effectively zero additional round-trips for TLS on repeat visits.

Certificate Validation

The browser validates the server’s certificate chain:

  1. Signature chain leads to a trusted CA root in the browser’s trust store.
  2. The SubjectAltName or CN matches api.example.com.
  3. The certificate is not expired.
  4. Optional: OCSP stapling confirms the certificate has not been revoked.

If validation fails, the browser shows an error and the connection is aborted — the request never reaches Kubernetes.

SNI: How the Load Balancer Knows Which Certificate to Present

The ServerName field in the ClientHello (Server Name Indication) tells the load balancer which hostname the client is connecting to. This allows a single IP address to host multiple TLS-protected domains — essential for cloud load balancers serving many customers on shared infrastructure.


Hop 3: The Cloud Load Balancer

The TCP connection terminates at the cloud load balancer (AWS ALB, GCP Application Load Balancer, Azure Application Gateway). This is the first component inside your infrastructure.

What the Cloud Load Balancer Does

Internet
  │
  │  Public IP: 203.0.113.42
  ▼
┌─────────────────────────────────────────────────────┐
│            Cloud Load Balancer (ALB)                │
│                                                     │
│  TLS termination (decrypts request)                 │
│  HTTP/2 → HTTP/1.1 conversion (if needed)           │
│  Health checks against backend targets              │
│  Sticky sessions (if configured)                    │
│  WAF rules (if attached)                            │
│  Access logs, metrics                               │
│                                                     │
│  Targets: [Node1:30080, Node2:30080, Node3:30080]  │
└────────────────────────┬────────────────────────────┘
                         │
           Selects a target node using round-robin
           (or least-connections / IP hash)
                         │
                         ▼
                   Node2:30080  (NodePort)

The load balancer forwards the decrypted (or re-encrypted) HTTP request to one of the Kubernetes nodes on the NodePort assigned to the Ingress controller Service (typically a random high port like 30080).

NodePort vs LoadBalancer Service Type

When a Kubernetes Service of type LoadBalancer is created, the cloud controller manager provisions a cloud load balancer automatically and configures it to forward traffic to the NodePort on each node. This is the standard path for production clusters on managed Kubernetes (EKS, GKE, AKS).

# What the cloud controller sees:
apiVersion: v1
kind: Service
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
spec:
  type: LoadBalancer        # triggers cloud LB provisioning
  ports:
    - port: 443
      targetPort: 443
      nodePort: 30080       # allocated by Kubernetes (or set manually)
  selector:
    app: ingress-nginx

The cloud load balancer’s health check hits Node:NodePort every few seconds. Nodes that fail health checks are removed from the rotation.

Added Headers

The load balancer typically adds or rewrites HTTP headers before forwarding:

X-Forwarded-For: 198.51.100.7        (original client IP)
X-Forwarded-Proto: https             (original protocol)
X-Forwarded-Port: 443
X-Request-ID: a1b2c3d4...            (correlation ID, if configured)
Host: api.example.com                (preserved or rewritten)

These headers are critical for your application to know the real client IP and original protocol — essential for rate limiting, audit logging, and redirect generation.


Hop 4: Ingress Controller

The packet arrives at a Kubernetes node on the NodePort (e.g., :30080). kube-proxy rules (covered in the next hop) DNAT this to the Ingress controller pod’s IP and port. The Ingress controller is the first Kubernetes-native component to handle the request.

What an Ingress Controller Is

An Ingress controller is an ordinary pod running a reverse proxy (nginx, Traefik, Envoy/Contour, HAProxy). It watches the Kubernetes API for Ingress resources and translates them into proxy configuration.

# Ingress resource — the routing rules
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: api-ingress
  namespace: production
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/ssl-redirect: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - api.example.com
      secretName: api-tls-cert      # TLS cert stored as a Secret
  rules:
    - host: api.example.com
      http:
        paths:
          - path: /v1/orders
            pathType: Prefix
            backend:
              service:
                name: order-service
                port:
                  number: 8080
          - path: /v1/users
            pathType: Prefix
            backend:
              service:
                name: user-service
                port:
                  number: 8080

What the Ingress Controller Does with This

The ingress-nginx controller translates the above into an nginx upstream configuration:

# Generated nginx config (simplified)
upstream order-service-production-8080 {
    server 10.244.1.5:8080;   # Pod IP (from Endpoints)
    server 10.244.2.8:8080;
    server 10.244.3.2:8080;
    keepalive 32;
}

server {
    listen 443 ssl http2;
    server_name api.example.com;

    ssl_certificate     /etc/tls/tls.crt;
    ssl_certificate_key /etc/tls/tls.key;

    location /v1/orders {
        proxy_pass         http://order-service-production-8080;
        proxy_set_header   Host $host;
        proxy_set_header   X-Real-IP $remote_addr;
        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_http_version 1.1;
        proxy_set_header   Connection "";  # enable keepalives
    }
}

Critically: the Ingress controller does not forward to the Service ClusterIP. It reads the Endpoints object directly and load-balances to pod IPs itself. This bypasses kube-proxy for the application traffic, giving the Ingress controller full control over load-balancing algorithm (round-robin, least-conn, ewma), health checking, and connection management.

The Endpoints Watch Loop

                    ┌─────────────────┐
                    │  kube-apiserver │
                    └────────┬────────┘
                             │ Watch /api/v1/namespaces/production/endpoints
                             │
                    ┌────────▼────────┐
                    │ Ingress Controller│
                    │                 │
                    │  on update:     │
                    │  reload upstreams│
                    └─────────────────┘

When a pod is added, removed, or fails a readiness probe, the Endpoints object is updated by the endpoint controller. The Ingress controller receives the watch event and updates its upstream configuration — nginx reloads without dropping connections (using the SO_REUSEPORT approach in newer versions).


Hop 5: kube-proxy and iptables/IPVS

When the Ingress controller sends a request to a backend pod (or when any pod communicates with a Service ClusterIP), it goes through kube-proxy’s rules.

Wait — didn’t we just say the Ingress controller bypasses kube-proxy by using pod IPs directly? Yes. But for the traffic from the cloud load balancer’s NodePort to the Ingress controller pod, and for any other service-to-service communication, kube-proxy rules are in the path.

What kube-proxy Does

kube-proxy does not proxy traffic. Despite the name, it does not sit in the data path at runtime. Instead, it programs the kernel’s netfilter (iptables) or IPVS tables to implement Service ClusterIP routing. The kernel does the actual packet rewriting — kube-proxy just keeps the rules up to date.

iptables Mode (Default)

For a Service with three pods:

Service: order-service
ClusterIP: 10.96.45.20
Port: 8080

Endpoints:
  10.244.1.5:8080  (Pod on Node1)
  10.244.2.8:8080  (Pod on Node2)
  10.244.3.2:8080  (Pod on Node3)

kube-proxy writes iptables rules like these:

# In the PREROUTING and OUTPUT chains:
-A KUBE-SERVICES -d 10.96.45.20/32 -p tcp --dport 8080 \
    -j KUBE-SVC-ORDERSERVICE

# In the KUBE-SVC-ORDERSERVICE chain (probabilistic selection):
-A KUBE-SVC-ORDERSERVICE -m statistic --mode random --probability 0.333 \
    -j KUBE-SEP-POD1
-A KUBE-SVC-ORDERSERVICE -m statistic --mode random --probability 0.500 \
    -j KUBE-SEP-POD2
-A KUBE-SVC-ORDERSERVICE \
    -j KUBE-SEP-POD3

# Each SEP chain does DNAT:
-A KUBE-SEP-POD1 -p tcp -j DNAT --to-destination 10.244.1.5:8080
-A KUBE-SEP-POD2 -p tcp -j DNAT --to-destination 10.244.2.8:8080
-A KUBE-SEP-POD3 -p tcp -j DNAT --to-destination 10.244.3.2:8080

When a packet arrives destined for 10.96.45.20:8080, the kernel traverses these chains, picks a pod probabilistically, and rewrites the destination IP:port (DNAT). The source IP is unchanged. The connection tracking (conntrack) table records the mapping so the return packets can be un-NATted.

The iptables Scaling Problem

iptables rules are evaluated linearly. A cluster with 1,000 Services and 10,000 endpoints has tens of thousands of iptables rules. Every packet traverses the relevant chains sequentially. At scale this causes:

  • Measurable per-packet latency increase
  • High CPU usage on kube-proxy during rule updates (the entire ruleset is replaced atomically)
  • Long update times (seconds) when many Services change simultaneously

IPVS Mode

IPVS (IP Virtual Server) is a kernel load balancer that uses a hash table instead of linear rule traversal. kube-proxy in IPVS mode creates a virtual server for each Service and real servers for each endpoint:

IP Virtual Server version 1.2.1
Prot LocalAddress:Port    Scheduler Flags
  -> RemoteAddress:Port   Forward Weight ActiveConn InActConn
TCP  10.96.45.20:8080     rr
  -> 10.244.1.5:8080      Masq    1      0          0
  -> 10.244.2.8:8080      Masq    1      0          0
  -> 10.244.3.2:8080      Masq    1      0          0

Lookup is O(1) via hash. IPVS supports richer scheduling algorithms: round-robin (rr), least-connection (lc), destination hash (dh), source hash (sh), shortest expected delay (sed). For clusters with thousands of services, IPVS mode is strongly recommended.

Cilium: Bypassing kube-proxy Entirely

Cilium (an eBPF-based CNI) can replace kube-proxy entirely. Instead of iptables/IPVS rules in the kernel’s netfilter path, Cilium attaches eBPF programs directly to network interfaces. These programs run earlier in the packet processing path, with lower overhead and finer-grained observability.

Without Cilium (kube-proxy):
  Packet → NIC → iptables PREROUTING → routing → iptables FORWARD → pod

With Cilium (kube-proxy replacement):
  Packet → NIC → eBPF XDP hook → (DNAT in eBPF) → pod
                  ↑
            runs before iptables, lower latency

Hop 6: CNI and Pod-to-Pod Networking

After kube-proxy (or Cilium) rewrites the destination to a pod IP, the packet must be delivered to the pod — possibly on a different node.

Pod IPs and the Flat Network Model

Kubernetes mandates a flat network model: every pod gets a unique IP, and every pod can communicate with every other pod directly by IP, without NAT, regardless of which node they are on.

The CNI (Container Network Interface) plugin implements this guarantee. How it does so depends on the plugin:

Overlay networks (Flannel VXLAN, Weave): The pod IP packet is encapsulated in a UDP frame (VXLAN) using the node’s physical IP. The receiving node decapsulates it and delivers to the pod. This adds ~50 bytes of overhead per packet and a small latency cost.

Node1 (10.0.1.10)                    Node2 (10.0.1.20)
  ┌─────────────────┐                  ┌─────────────────┐
  │ Pod A           │                  │ Pod B           │
  │ 10.244.1.5      │                  │ 10.244.2.8      │
  └──────┬──────────┘                  └──────┬──────────┘
         │ veth0                              │ veth0
         ▼                                    ▼
    cni0 bridge                          cni0 bridge
         │                                    │
         ▼                                    ▼
    flannel0 (VXLAN)  ──────────────►  flannel0 (VXLAN)
         │                                    │
         ▼                                    ▼
      eth0 (10.0.1.10)                   eth0 (10.0.1.20)
              [outer UDP/VXLAN packet]

Underlay/BGP networks (Calico, Cilium native): Routes are advertised via BGP directly on the physical network. No encapsulation — the pod IP is natively routable on the underlying network. Lower overhead, requires network infrastructure that supports BGP.

Node1: advertises 10.244.1.0/24 via BGP
Node2: advertises 10.244.2.0/24 via BGP

Packet from Pod A (10.244.1.5) to Pod B (10.244.2.8):
  Node1's kernel routes to 10.244.2.0/24 → next hop: Node2 (10.0.1.20)
  Physical network delivers to Node2
  Node2's kernel routes 10.244.2.8 → veth pair → Pod B

The veth Pair

Every pod is connected to the node via a veth pair — a virtual Ethernet cable with two ends. One end (eth0) is inside the pod’s network namespace. The other end is on the node (named something like vethXXXXXXX) and connected to the CNI bridge or directly to the node’s routing table.

# On the node:
ip link show type veth
# 7: veth3a8b2c1@if2: <BROADCAST,MULTICAST,UP,LOWER_UP> ...

# The pod sees the other end as eth0:
# (kubectl exec into pod)
kubectl exec -it order-pod -- ip addr
# 2: eth0@if7: <BROADCAST,MULTICAST,UP> inet 10.244.1.5/24

Packets entering the pod’s eth0 come from the node via the veth pair. Packets leaving the pod’s eth0 exit via the veth pair to the node, where the node’s routing table forwards them.


Hop 7: The Pod Receives the Request

The packet finally arrives at the pod. Inside the pod’s network namespace, the kernel’s TCP stack processes it. The container listens on a port (e.g., :8080), and the process handling that port (your application) receives the connection.

What the Pod Sees

The pod sees the request as coming from the Ingress controller pod’s IP (if the Ingress controller proxies without SNAT) or from the node’s IP (if SNAT was applied). The original client IP is in the X-Forwarded-For header — your application must read this header if it needs the real client IP.

// Reading the real client IP in Go
func clientIP(r *http.Request) string {
    // Check headers set by load balancer / ingress controller
    if xff := r.Header.Get("X-Forwarded-For"); xff != "" {
        // XFF can be a comma-separated list; the first is the original client
        parts := strings.Split(xff, ",")
        return strings.TrimSpace(parts[0])
    }
    if xri := r.Header.Get("X-Real-Ip"); xri != "" {
        return xri
    }
    // Fall back to direct connection IP (will be node/proxy IP)
    ip, _, _ := net.SplitHostPort(r.RemoteAddr)
    return ip
}

Request Headers by the Time They Reach the Pod

GET /v1/orders HTTP/1.1
Host: api.example.com
X-Forwarded-For: 198.51.100.7, 203.0.113.42
X-Forwarded-Proto: https
X-Request-ID: a1b2c3d4-e5f6-7890-abcd-ef1234567890
X-Real-Ip: 198.51.100.7
User-Agent: Mozilla/5.0 ...
Accept: application/json
Authorization: Bearer eyJhbGci...

Each intermediate hop has added headers. The application sees the full request enriched with routing and tracing metadata.


The Return Path

The response travels back through most of the same layers:

  1. Pod → veth → node routing — the response packet has source IP 10.244.1.5:8080 and destination IP of the Ingress controller pod.
  2. CNI delivers to Ingress controller pod — across nodes if needed, via overlay or BGP routes.
  3. Ingress controller → cloud load balancer — the Ingress controller sends the HTTP response back on the upstream TCP connection. The cloud load balancer forwards it to the browser on the established TLS connection.
  4. Cloud load balancer → browser — the TLS-encrypted response is delivered over the original TCP connection.
  5. Browser renders — the response body is decoded and rendered.

The conntrack table on each node tracks the NAT mappings, so return packets are un-NATted correctly at each hop.


Timing Budget: Where the Latency Goes

For a request from a user 50ms RTT from your load balancer, serving a simple API response in 5ms of application time:

Hop Latency Notes
DNS resolution 0–5 ms Cache hit; 50–200ms cold
TCP handshake 50 ms 1 RTT to load balancer
TLS 1.3 handshake 50 ms 1 RTT (0-RTT on resume: 0ms)
Cloud LB → Node 1–2 ms Internal network
kube-proxy DNAT < 0.1 ms Kernel netfilter
CNI (same node) < 0.1 ms veth pair
CNI (cross-node) 0.5–2 ms Overlay encap or BGP routing
Application processing 5 ms Your code
Return path ~50 ms Symmetric to request path
Total (warm) ~156 ms
Total (cold DNS) ~300 ms Cold DNS + TLS

The dominant latency is almost always the client’s RTT to the load balancer — which is why CDNs and regional load balancer endpoints exist. Everything inside the cluster is measured in sub-millisecond to low-millisecond increments.


Failure Modes at Each Hop

Understanding the path makes failures diagnosable:

DNS Failure

Symptom: curl: (6) Could not resolve host: api.example.com Causes: TTL expired + authoritative nameserver down; misconfigured NS records; split-horizon DNS misconfiguration. Diagnose: dig api.example.com @8.8.8.8 +trace to walk the full resolution chain.

TLS Failure

Symptom: curl: (60) SSL certificate problem: certificate has expired Causes: Expired certificate; certificate not covering the hostname (SAN mismatch); CA not trusted. Diagnose: openssl s_client -connect api.example.com:443 -servername api.example.com to inspect the full certificate chain.

Cloud Load Balancer Health Check Failure

Symptom: 503 from the load balancer; all nodes removed from rotation. Causes: NodePort not reachable (firewall rules); Ingress controller pods not running; security group blocks health check probe IPs. Diagnose: Cloud LB health check status in the console; check kubectl get pods -n ingress-nginx.

Ingress Controller Misconfiguration

Symptom: 404 from nginx; correct hostname but wrong path matching. Causes: Ingress pathType: Prefix vs Exact mismatch; missing ingressClassName; wrong namespace. Diagnose: kubectl describe ingress api-ingress -n production; check Ingress controller logs.

Pod Not Ready / Endpoint Missing

Symptom: Intermittent 502/503; some requests succeed, some fail. Causes: Pod failing readiness probe but not yet removed from Endpoints; pod restarting; terminationGracePeriodSeconds too short. Diagnose: kubectl get endpoints order-service -n production; kubectl describe pod; check readiness probe configuration.

CNI / Network Plugin Failure

Symptom: Pod-to-pod communication fails; DNS lookups from pods fail. Causes: CNI plugin crashed; VXLAN MTU mismatch (common with overlay networks); iptables rules corrupted. Diagnose: kubectl exec into a pod and curl another pod’s IP directly; check CNI plugin DaemonSet logs; ip route on the node.

kube-proxy / iptables Rules Stale

Symptom: Requests to ClusterIP fail; Service exists but connection refused. Causes: kube-proxy pod crashed; iptables rules not updated after pod scaling. Diagnose: iptables -t nat -L KUBE-SERVICES | grep <service-ip> on the node; check kube-proxy pod logs.


Key Configuration Points Across the Stack

Connection Draining (Graceful Shutdown)

When a pod is deleted or evicted, requests in flight must complete before the pod disappears:

spec:
  terminationGracePeriodSeconds: 30   # Kubernetes waits this long
  containers:
    - name: api
      lifecycle:
        preStop:
          exec:
            command: ["/bin/sh", "-c", "sleep 5"]  # allow LB to deregister

The preStop sleep gives the Ingress controller and kube-proxy time to remove the pod from their routing tables before the process receives SIGTERM. Without it, a brief window exists where the pod is terminating but still receives traffic — causing connection resets.

ReadinessProbe vs LivenessProbe

readinessProbe:
  httpGet:
    path: /healthz/ready
    port: 8080
  initialDelaySeconds: 5
  periodSeconds: 5
  failureThreshold: 3
  # Pod removed from Endpoints if this fails — stops NEW traffic

livenessProbe:
  httpGet:
    path: /healthz/live
    port: 8080
  initialDelaySeconds: 15
  periodSeconds: 10
  failureThreshold: 3
  # Pod restarted if this fails — last resort recovery

The readiness probe gates Endpoint membership — a failing readiness probe removes the pod from the Endpoints object, which propagates to the Ingress controller’s upstream configuration (within seconds). The liveness probe gates pod restart — it should only fail for states the pod cannot recover from on its own.

Service topologyAwareRouting

For latency-sensitive services, route traffic preferentially to pods in the same availability zone:

apiVersion: v1
kind: Service
metadata:
  name: order-service
  annotations:
    service.kubernetes.io/topology-mode: "Auto"
spec:
  ports:
    - port: 8080
  selector:
    app: order-service

With topologyMode: Auto, the EndpointSlice controller adds topology hints, and kube-proxy prefers endpoints in the same zone. Cross-zone traffic (which has inter-AZ data transfer costs and higher latency) is only used when local endpoints are unavailable.


Key Takeaways

The journey from browser to pod crosses more than a dozen distinct system boundaries. Each one is independently configurable, independently observable, and independently failable. Knowing the full path gives you three capabilities:

  1. Precise failure diagnosis. A 502 from the cloud load balancer means something different from a 502 from the Ingress controller, which means something different from a connection reset at the pod. The error and its origin tell you exactly where to look.

  2. Informed performance optimisation. The latency budget shows that DNS and TLS dominate on cold requests. CDN termination, TLS session resumption, and HTTP/2 multiplexing have more impact than most in-cluster optimisations.

  3. Safe configuration changes. Adding a readiness probe endpoint, tuning terminationGracePeriodSeconds, switching kube-proxy to IPVS mode, or replacing the CNI plugin each affects a specific layer. Knowing which layer lets you reason about the change’s blast radius before you make it.

The request flow is not magic. It is a chain of well-specified components, each doing one job, each leaving its mark in logs and metrics you can read. Map the chain, and the cluster becomes legible.