Skip to content

Networking

First PublishedLast UpdatedByAtif Alam

Every pod in Kubernetes gets its own IP address. Networking ties pods together and exposes them to the outside world.

Kubernetes has three rules:

  1. Every pod can communicate with every other pod (no NAT).
  2. Nodes can communicate with all pods (and vice versa).
  3. The IP a pod sees for itself is the same IP others see for it.

This flat network means you don’t need to map ports between containers. A CNI plugin implements these rules and assigns the pod network interface. The sections below compare common CNIs; for AWS VPC CNI specifics (IP exhaustion, ENIs), see EKS troubleshooting cheat sheet.

CNI plugins compared (Calico, Cilium, Flannel)

Section titled “CNI plugins compared (Calico, Cilium, Flannel)”
CNIDatapath focusNetworkPolicykube-proxy replacementTypical fit
FlannelSimple overlay (often VXLAN)No built-in enforcement — pair with another policy layer if you need rulesUsually no — kube-proxy still programs ServicesSmall clusters, labs, “just get pod IP working”
CalicoBGP or overlay options; mature ecosystemYes — broad L3/L4 policy; policy-only mode can sit beside cloud CNIs (for example on EKS)Optional — can offload some Service pathsTeams needing policy without committing to full eBPF datapath
CiliumeBPF-heavy datapathYes — L3/L4 and L7 visibility where enabledOften yes — can replace kube-proxy for ServicesPlatforms wanting observability, Hubble, service mesh–like features at CNI layer

Rule of thumb: Flannel is enough when you trust every workload and only need L3 connectivity. Add Calico or Cilium when you need default-deny, egress controls, or eBPF-level introspection. Pick based on ops maturity and vendor support for your distro — not feature charts alone.

Without cloud-specific ENI details, the mental model is:

Pod A (pod netns) -> veth -> node routing
-> underlay (VPC, DC fabric, or overlay tunnel)
-> remote node -> veth -> Pod B (pod netns)

Overlays (VXLAN, Geneve) encapsulate pod IPs inside node-to-node packets; underlays (for example cloud VPC routing to pod CIDRs) may expose pod IPs directly on the network. Either way, Services still provide stable virtual IPs — kube-proxy or a CNI datapath programs how ClusterIP maps to backends. For kube-proxy modes and EndpointSlices, see Services and endpoints (and the same page links here for the generic path above).

Pods are ephemeral — they come and go. A Service provides a stable address that routes traffic to a set of pods matched by a label selector.

Internal-only. Reachable from inside the cluster.

apiVersion: v1
kind: Service
metadata:
name: my-app
spec:
selector:
app: my-app
ports:
- port: 80
targetPort: 8080

Other pods reach this service at my-app:80 or my-app.default.svc.cluster.local:80.

EndpointSlices are the modern backing store for Service endpoints (replacing large single Endpoints objects at scale). The kube-proxy (or CNI datapath) watches slices and programs rules when pods become Ready/Unready or restart — that churn is a common cause of short 503 windows during rollouts if clients keep stale connections.

Terminal window
kubectl get endpointslices -n default -l kubernetes.io/service-name=my-app

See Services and endpoints for kube-proxy modes and debugging.

ModeNotes
iptablesCommon default; rule count grows with services and backends
ipvsHash-based dispatch; often better at very large service counts
eBPF / no kube-proxyCilium and similar program the datapath directly — rich observability
Terminal window
kubectl get cm kube-proxy-config -n kube-system -o yaml | grep mode

Exposes the service on a static port on every node’s IP. Useful for development or when you don’t have a cloud load balancer.

spec:
type: NodePort
ports:
- port: 80
targetPort: 8080
nodePort: 30080 # accessible at <NodeIP>:30080

Provisions an external load balancer (on cloud providers). Traffic from the internet reaches the LB, which forwards to the service.

spec:
type: LoadBalancer
ports:
- port: 80
targetPort: 8080

Use service types by exposure level and protocol needs:

NeedRecommended choiceWhy
Internal service-to-service trafficClusterIPStable DNS inside cluster, no external exposure
Public HTTP/HTTPS appIngress/Gateway + ClusterIP backendCentralized TLS/routing, fewer public entrypoints
Public non-HTTP (TCP/UDP) serviceLoadBalancerDirect external L4 access from cloud LB
Quick local/dev accessNodePortSimple direct testing via <NodeIP>:nodePort

For most new production setups:

  1. Create app services as ClusterIP.
  2. Expose external web traffic through Ingress (or Gateway API).
  3. Use LoadBalancer at the edge (often for the ingress controller), not per service unless required.
  4. Keep NodePort mainly for development, labs, or specific infrastructure constraints.

Often, yes for HTTP/HTTPS workloads.

  • Ingress defines Layer 7 routing rules (host/path/TLS behavior).
  • A LoadBalancer Service usually exposes the Ingress controller externally.

Typical flow:

Internet
-> Cloud LoadBalancer (Service type: LoadBalancer for ingress controller)
-> Ingress Controller
-> ClusterIP Services
-> Pods

Use only Service type: LoadBalancer (without Ingress) when you need simple direct exposure, especially for single-service or non-HTTP TCP/UDP use cases.

An Ingress manages external HTTP/HTTPS access to services. It provides:

  • Host-based routingapi.example.com goes to one service, app.example.com to another.
  • Path-based routing/api goes to the backend, / to the frontend.
  • TLS termination — HTTPS at the edge.

For HTTP semantics (status codes, headers, timeouts at the edge) and TLS termination patterns, see HTTP for Operators. For certificate lifecycle on AWS (ACM) and pointers to cert-manager, see TLS and Certificates and Operators (cert-manager).

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: my-ingress
spec:
rules:
- host: app.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: frontend
port:
number: 80
- path: /api
pathType: Prefix
backend:
service:
name: backend
port:
number: 80

An Ingress needs an Ingress Controller (e.g. NGINX Ingress Controller, Traefik) actually running in the cluster to work. For controller selection, TLS/mTLS patterns, and cert-manager wiring, see Ingress Controllers.

Kubernetes runs an internal DNS service (CoreDNS). Every Service gets a DNS name:

  • <service-name> — within the same namespace.
  • <service-name>.<namespace>.svc.cluster.local — fully qualified.

Pods can resolve service names automatically; no hardcoded IPs needed.

Under load, DNS becomes a shared dependency for the whole cluster. What tends to break first:

  • Too few CoreDNS replicas for cluster QPS — elevated latency on every dependency that resolves names.
  • Upstream forwarder issues (corporate resolver, VPC DNS) — timeouts bubble up as app errors.
  • NodeLocal DNSCache misconfiguration — faster local cache when correct; black holes when not.
  • Large ndots / search list — amplifies query volume from musl/glibc resolvers.

Mitigations: right-size CoreDNS HPA, validate CoreDNS ConfigMap (forward plugin), consider NodeLocal for high-QPS clusters, and keep an eye on memory for caches.

By default, all pods can talk to all other pods. NetworkPolicies restrict traffic (like a firewall). They require a CNI plugin that supports them (e.g. Calico, Cilium).

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-only
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- port: 8080

This policy says: only pods labeled app: frontend can reach pods labeled app: backend on port 8080.

For least privilege, start from deny all ingress for a namespace (or tenant), then add explicit NetworkPolicy resources for each allowed flow. Example deny-all ingress:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: team-a
spec:
podSelector: {}
policyTypes: [Ingress]

Add additional policies per app to permit required sources and ports. See Network policies for metadata egress blocks and CNI notes.

On AWS, Services type: LoadBalancer and Ingress (with the AWS Load Balancer Controller) create NLB or ALB resources. See Elastic Load Balancing and Route 53 for public DNS in front of those endpoints.

For L7 routing, retries, and mTLS inside the cluster, Istio (and other meshes) sit alongside Ingress. See Istio.

Both solve north–south HTTP(S) routing into the cluster, but they differ in API shape and ownership — not in “magic performance.”

TopicIngress (Ingress, IngressClass)Gateway API (Gateway, GatewayClass, HTTPRoute, …)
MaturityUbiquitous; familiar to most platform teamsNewer; requires a controller that implements Gateway API (often the same vendor as your Ingress controller)
RolesAnnotations-heavy; host/path rules on one objectRoute objects attach to a Gateway — clearer split between platform (Gateway, TLS, shared listener) and app teams (HTTPRoute)
ExtensibilityVendor-specific annotations for advanced behaviorTyped route resources and policy attachments (implementation-dependent)
MeshesClassic Ingress Gateway patternMeshes (for example Istio) increasingly support Gateway API as a front door alongside or instead of Ingress — see Istio

When to stay on Ingress: existing controllers, charts, and team muscle already work; you only need host/path TLS termination.

When to adopt Gateway API: multi-team clusters where delegation (who owns listeners vs routes), TLS policy, and portable route objects reduce annotation sprawl — after your chosen controller is GA for the features you need.

Controller selection, TLS/mTLS, and AWS Load Balancer Controller patterns stay on Ingress controllers.