Kubernetes Networking & Ingress
Quick answer
Understand how traffic flows inside a Kubernetes cluster and into it — pod networking, Service DNS, Network Policies, Ingress controllers, and TLS termination.
- How Pod Networking Works
- Services Internals
- Network Policies — Restricting Traffic
- Ingress — Routing External Traffic
- TLS Termination
intermediate · 55 min
Before you begin
- Kubernetes core concepts — Pods, Deployments, Services
- Basic networking — IP addresses, DNS, HTTP/HTTPS
Kubernetes Networking & Ingress
Understanding Kubernetes networking means understanding how three layers work together: pod-to-pod networking across nodes, Service-based load balancing and discovery, and Ingress for routing external traffic. Each layer builds on the previous one.
How Pod Networking Works
Kubernetes enforces a simple networking model:
- Every Pod gets a unique IP address
- Pods on the same node and Pods on different nodes can all communicate directly — no NAT, no port mapping
- Pods can reach each other's IP without knowing which node they're on
This flat network model is implemented by a CNI plugin (Container Network Interface). The cluster admin installs one when the cluster is set up:
| CNI Plugin | Notes |
|---|---|
| Calico | Most widely deployed; supports Network Policies |
| Cilium | eBPF-based; best performance and observability |
| Flannel | Simple overlay network; does NOT support Network Policies |
| WeaveNet | Easy setup; supports Network Policies |
If your cluster uses Flannel, NetworkPolicy objects are accepted by the API server but have no effect — there's no enforcement. Use Calico or Cilium if you need Network Policies.
Why pods have their own IPs
In Docker, containers share the host network by default unless you create a user-defined bridge. In Kubernetes, each Pod gets its own network namespace with its own IP. This means:
- Port 8080 on
pod-ais different from port 8080 onpod-b— no conflicts - Two containers inside the same Pod share an IP — they communicate via
localhost - No NAT between pods — Pod A can connect to Pod B's IP directly
Services Internals
A Service provides a stable virtual IP (ClusterIP) and DNS name for a group of Pods. When you create a Service, kube-proxy on each node installs iptables (or IPVS) rules that forward traffic to one of the matching Pod IPs.
kubectl get endpoints api # See the actual pod IPs behind a service
# NAME ENDPOINTS AGE
# api 10.244.0.5:80,10.244.1.3:80 5mIf a Pod fails its readiness probe, its IP is removed from the Endpoints list automatically. Traffic stops reaching it without you doing anything.
Service DNS
CoreDNS runs in every Kubernetes cluster and provides in-cluster DNS. Every Service gets a DNS record:
<service>.<namespace>.svc.cluster.local
From any Pod in the cluster:
1# These all resolve to the same ClusterIP
2curl http://api # Same namespace only
3curl http://api.default # Any namespace
4curl http://api.default.svc.cluster.local # Fully qualified
5
6# Check what DNS settings a pod sees
7kubectl exec <pod> -- cat /etc/resolv.conf
8# search default.svc.cluster.local svc.cluster.local cluster.local
9# nameserver 10.96.0.10 ← CoreDNS ClusterIPThe search domains are why curl http://api works — the DNS client appends the search suffixes automatically.
Headless Services
A headless Service (no ClusterIP) returns the individual Pod IPs directly from DNS instead of a single virtual IP. Used by StatefulSets so each replica has a stable DNS name.
1apiVersion: v1
2kind: Service
3metadata:
4 name: db
5spec:
6 clusterIP: None # Headless — no ClusterIP
7 selector:
8 app: db
9 ports:
10 - port: 5432DNS for a headless Service returns all Pod IPs. StatefulSet pods get individual DNS names:
db-0.db.default.svc.cluster.local
db-1.db.default.svc.cluster.local
Network Policies — Restricting Traffic
By default, all Pods in a cluster can talk to all other Pods. Network Policies restrict this.
A Network Policy selects Pods using podSelector and defines allowed ingress (inbound) and/or egress (outbound) traffic.
Default deny all ingress
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: deny-all-ingress
5 namespace: production
6spec:
7 podSelector: {} # Selects ALL Pods in the namespace
8 policyTypes:
9 - Ingress
10 # No ingress rules = deny all inbound trafficAfter applying this, nothing can reach any Pod in the production namespace unless another NetworkPolicy explicitly allows it.
Allow only from within the same namespace
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: allow-same-namespace
5 namespace: production
6spec:
7 podSelector: {}
8 policyTypes:
9 - Ingress
10 ingress:
11 - from:
12 - podSelector: {} # Any pod in the same namespaceAllow the database to accept connections only from the API
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: db-policy
5 namespace: production
6spec:
7 podSelector:
8 matchLabels:
9 app: db
10 policyTypes:
11 - Ingress
12 ingress:
13 - from:
14 - podSelector:
15 matchLabels:
16 app: api # Only pods with app=api can reach db
17 ports:
18 - protocol: TCP
19 port: 5432Allow egress to DNS only
1apiVersion: networking.k8s.io/v1
2kind: NetworkPolicy
3metadata:
4 name: allow-dns-egress
5spec:
6 podSelector:
7 matchLabels:
8 app: api
9 policyTypes:
10 - Egress
11 egress:
12 - ports:
13 - protocol: UDP
14 port: 53 # DNS
15 - protocol: TCP
16 port: 53Always include a DNS egress rule when writing egress policies. Without it, the Pod can't resolve hostnames.
Ingress — Routing External Traffic
A Service of type LoadBalancer creates a separate cloud load balancer per service — expensive at scale. An Ingress routes HTTP/HTTPS traffic from a single load balancer to multiple Services based on hostname and path.
Internet → Load Balancer → Ingress Controller → Service → Pods
Ingress Controller
An Ingress object is just configuration. You need an Ingress Controller — a running Pod that reads Ingress objects and actually routes traffic.
1# Install nginx Ingress Controller via Helm
2helm repo add ingress-nginx https://kubernetes.github.io/ingress-nginx
3helm repo update
4helm install ingress-nginx ingress-nginx/ingress-nginx \
5 --namespace ingress-nginx \
6 --create-namespace
7
8# Verify
9kubectl get pods -n ingress-nginx
10kubectl get service ingress-nginx-controller -n ingress-nginxPopular Ingress controllers:
| Controller | Notes |
|---|---|
| ingress-nginx | Most widely used; rich annotation support |
| Traefik | Built-in Let's Encrypt; good for small clusters |
| AWS ALB controller | Native AWS Application Load Balancer |
| Contour | Envoy-based; good performance |
A basic Ingress
1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4 name: api-ingress
5spec:
6 ingressClassName: nginx
7 rules:
8 - host: api.example.com
9 http:
10 paths:
11 - path: /
12 pathType: Prefix
13 backend:
14 service:
15 name: api
16 port:
17 number: 80Path-based routing
1spec:
2 ingressClassName: nginx
3 rules:
4 - host: example.com
5 http:
6 paths:
7 - path: /api
8 pathType: Prefix
9 backend:
10 service:
11 name: api
12 port:
13 number: 80
14 - path: /
15 pathType: Prefix
16 backend:
17 service:
18 name: frontend
19 port:
20 number: 80Multiple hostnames
1spec:
2 ingressClassName: nginx
3 rules:
4 - host: api.example.com
5 http:
6 paths:
7 - path: /
8 pathType: Prefix
9 backend:
10 service:
11 name: api
12 port:
13 number: 80
14 - host: admin.example.com
15 http:
16 paths:
17 - path: /
18 pathType: Prefix
19 backend:
20 service:
21 name: admin
22 port:
23 number: 80TLS Termination
TLS is terminated at the Ingress Controller. The Ingress references a kubernetes.io/tls Secret containing the certificate and key.
# Create a TLS secret from a certificate file
kubectl create secret tls api-tls-cert \
--cert=tls.crt \
--key=tls.key1apiVersion: networking.k8s.io/v1
2kind: Ingress
3metadata:
4 name: api-ingress
5 annotations:
6 nginx.ingress.kubernetes.io/ssl-redirect: "true"
7spec:
8 ingressClassName: nginx
9 tls:
10 - hosts:
11 - api.example.com
12 secretName: api-tls-cert # The Secret created above
13 rules:
14 - host: api.example.com
15 http:
16 paths:
17 - path: /
18 pathType: Prefix
19 backend:
20 service:
21 name: api
22 port:
23 number: 80cert-manager — automatic certificate management
For production, use cert-manager to automatically issue and renew Let's Encrypt certificates:
helm repo add jetstack https://charts.jetstack.io
helm install cert-manager jetstack/cert-manager \
--namespace cert-manager \
--create-namespace \
--set crds.enabled=trueWith cert-manager, add a single annotation to the Ingress:
metadata:
annotations:
cert-manager.io/cluster-issuer: "letsencrypt-prod"cert-manager handles the ACME challenge, issues the certificate, and rotates it automatically.
Debugging Networking
1# Check if a service has endpoints (pods are passing readiness probes)
2kubectl get endpoints <service-name>
3
4# Test connectivity from inside the cluster
5kubectl run debug --image=busybox --rm -it --restart=Never -- sh
6# Inside the debug pod:
7wget -O- http://api # Test Service DNS
8nslookup api.default.svc.cluster.local # Test DNS resolution
9wget -O- http://10.244.0.5:80 # Test direct pod IP
10
11# Check Ingress events
12kubectl describe ingress <name>
13
14# Check Ingress controller logs
15kubectl logs -n ingress-nginx -l app.kubernetes.io/name=ingress-nginxFrequently Asked Questions
How does a Service actually route traffic?
The Service is a stable virtual address; the routing is done by kube-proxy or an eBPF dataplane programming rules on every node to distribute connections across the Pod IPs currently in the endpoint list. That list is maintained by the EndpointSlice controller from the Service's selector, which is why readiness affects routing.
Why does my Service have no endpoints?
The selector matches no running Pod's labels, or the matching Pods are not ready. Compare the selector against the Pod labels rather than the Deployment's, since the Pod template produces them. If labels match, check readiness — a Pod that is not ready is deliberately excluded.
When do I need ClusterIP versus NodePort versus LoadBalancer?
ClusterIP for internal traffic, which is most Services. LoadBalancer for external traffic on a cloud that provisions one. NodePort mostly as a building block or for bare metal without a load balancer — exposing a NodePort directly to the internet is rarely what you want.
Do NetworkPolicies work on any cluster?
Only where the CNI enforces them. NetworkPolicy is an API any CNI may implement, and some do not, so the objects are accepted and silently ignored. Confirm your CNI enforces policy before relying on it — silent acceptance is the failure mode that catches people.
What's Next
- Kubernetes Storage, ConfigMaps & Secrets — how to inject configuration and persist data across pod restarts
- Kubernetes RBAC & Security — service accounts, roles, and pod security
Official References
- Ingress — path types, backends and the ingress controller contract
- Network Policies — selector semantics and default-deny behaviour
- DNS for Services and Pods — the DNS record schema and ndots/search-path behaviour
- CoreDNS plugins — the plugin chain, including cache, forward and autopath
Next in Kubernetes Foundations
Kubernetes Storage, ConfigMaps & Secrets
We built Podscape to simplify Kubernetes workflows like this — logs, events, and cluster state in one interface, without switching tools.
Struggling with this in production?
We help teams fix these exact issues. Our engineers have deployed these patterns across production environments at scale.