Networking
for K8s
& Docker
From subnets and OSI layers to Ingress controllers, CoreDNS, service meshes, and zero-trust network policies. A complete field guide for DevOps engineers, beginner to intermediate.
Basic Networking Concepts
Before diving into Docker or Kubernetes, you need a firm grip on the building blocks. Container networking is just regular networking under a layer of abstraction — understanding the fundamentals prevents mystery when things break.
A unique numerical label assigned to every device (or container) on a network. IPv4 uses 32-bit addresses (e.g. 192.168.1.10). IPv6 uses 128-bit addresses (e.g. fd00::1). Every pod in Kubernetes gets its own IP.
A number (1–65535) that identifies a specific process on a host. Think of the IP as the building address and the port as the apartment number. 80 = HTTP, 443 = HTTPS, 8080 = common app port.
Rules for how data is formatted and transmitted. TCP — reliable, ordered, connection-based (web, APIs). UDP — fast, connectionless, no delivery guarantee (DNS queries, video streaming).
Subnetting & CIDR
A subnet divides a larger network into smaller segments. CIDR (Classless Inter-Domain Routing) notation expresses an IP range compactly. This is the notation Kubernetes uses everywhere for pod and service CIDRs.
| CIDR Notation | Subnet Mask | Total IPs | Usable Hosts | Typical Use |
|---|---|---|---|---|
/8 | 255.0.0.0 | 16,777,216 | 16,777,214 | Large cloud VPCs |
/16 | 255.255.0.0 | 65,536 | 65,534 | Kubernetes cluster CIDR |
/24 | 255.255.255.0 | 256 | 254 | Single node subnet, Docker bridge |
/28 | 255.255.255.240 | 16 | 14 | Small service subnet |
/32 | 255.255.255.255 | 1 | 1 | Single host / pod IP |
10.244.0.0/16 (Flannel), Service CIDR 10.96.0.0/12 (default kubeadm). These are private RFC 1918 ranges — they never appear on the public internet.Ports & Common Protocols
OSI & TCP/IP Models
The OSI model is a conceptual framework that describes how data travels across a network in 7 layers. When someone says "Layer 4 load balancer" or "Layer 7 routing" — this is what they mean. Kubernetes Ingress operates at Layer 7. Services operate at Layer 4.
TCP vs UDP — When to Use What
- Connection-oriented (3-way handshake)
- Guaranteed delivery, ordered packets
- Error detection and retransmission
- Slower due to overhead
- Use for: HTTP/S, gRPC, databases, APIs
- Connectionless — fire and forget
- No delivery guarantee, no order
- Minimal overhead, very fast
- Lost packets are NOT retransmitted
- Use for: DNS, video streams, game data
IP Addressing, Subnets & DNS
DNS (Domain Name System) translates human-readable names like api.myapp.com into IP addresses. In Kubernetes, CoreDNS does this for every service — so instead of remembering 10.96.45.12, you write user-service.production.svc.cluster.local.
- App calls
db.default.svc.cluster.local - Query goes to CoreDNS (ClusterIP:
10.96.0.10) - CoreDNS returns the Service ClusterIP
- Traffic routes to one of the pods behind the Service
- kube-proxy handles the final NAT/iptables routing
10.0.0.0/8— largest private block (Kubernetes pods)172.16.0.0/12— Docker's default bridge range192.168.0.0/16— home/office routers- These never route on the public internet
- All container networks use these ranges
NAT — Network Address Translation
NAT allows many private IP addresses to share one public IP. Docker uses NAT to let containers reach the internet via the host's public IP. Kubernetes uses SNAT (Source NAT) when pods reach external services. iptables rules handle this transparently.
Docker Network Overview
When Docker starts, it creates a virtual network called docker0 on the host — a software bridge operating at Layer 2. Every container gets a virtual ethernet (veth) pair: one end inside the container's private network namespace, the other attached to docker0. Containers on the same bridge can talk to each other; traffic to the host or internet is NAT'd.
172.17.0.2
172.17.0.3
172.17.0.4
bridge network can only communicate via IP address — no DNS name resolution between them. Always create a user-defined bridge network (or use Docker Compose) to get automatic DNS by container name.Docker Network Drivers
Software bridge on the host. Containers on the same bridge can communicate. Requires port mapping to reach from outside. Use for: local dev, single-host setups. DNS by container name works on user-defined bridges.
Container shares the host's network stack directly — no isolation. The container binds ports on the host directly. Use for: performance-critical apps where network overhead matters. No port mapping needed.
Spans multiple Docker hosts (Docker Swarm mode). Encapsulates packets using VXLAN to create a virtual L2 network across hosts. Use for: Swarm multi-node deployments. Not commonly used with Kubernetes.
Gives each container its own MAC address, making it appear as a physical device on the network. Use for: legacy apps that require a real NIC. Complex to configure.
No network interface configured. Container is completely network-isolated. Use for: batch jobs, security-sensitive workloads, or when networking is handled externally.
Similar to macvlan but shares the parent MAC address. Useful when the switch limits MAC address counts. Use for: data center environments with strict MAC policies.
Docker Compose & Service Communication
Docker Compose automatically creates a user-defined bridge network for your project and connects all services to it. Services can reach each other using their service name as the hostname. This is the foundation of multi-container communication on a single host.
postgres) is only on backend-net — not on frontend-net. This means even if the frontend is compromised, it cannot directly reach the database. The API bridges both networks and acts as the only authorized caller.How Service Name DNS Works in Compose
Port Mapping & EXPOSE
EXPOSE 8080 in a Dockerfile is documentation only. It tells other developers which port the app listens on, but does NOT actually publish it. Think of it as a label, not a firewall rule.
-p 8080:3000 actually publishes the port. Format: HOST_PORT:CONTAINER_PORT. Traffic hitting the host on port 8080 is forwarded to port 3000 inside the container via iptables DNAT rules.
127.0.0.1 for local-only services: -p 127.0.0.1:5432:5432.Kubernetes Network Model
Kubernetes networking is built on a set of fundamental requirements that every CNI plugin must implement. Understanding these rules is key to reasoning about why things work the way they do.
Every pod in the cluster gets its own IP address from the pod CIDR range. You never need to map ports between containers in the same pod — they share a network namespace and communicate via localhost.
Any pod can communicate with any other pod in the cluster using its pod IP — no NAT, no port mappings. This flat network model is enforced by the CNI plugin across nodes.
When pod A talks to pod B, the source IP seen by pod B is the actual IP of pod A — not NAT'd. This matters for logging, security, and network policies that match on source IP.
Node-level agents (kubelet, kube-proxy, monitoring daemons) can reach all pods on all nodes. This enables health checks, metrics scraping, and log collection.
Pod-to-Pod Communication Across Nodes
10.244.0.5
10.244.1.7
Pod Networking
A Kubernetes Pod is a group of one or more containers that share a network namespace — meaning they share the same IP address, the same set of ports, and the same localhost. They communicate with each other via localhost, just like processes on the same machine.
Every pod starts an invisible "pause" container (also called infra or sandbox container). Its job is to hold the network namespace open. Even if app containers restart, the pod IP stays the same because the pause container never stops.
containerPort in a Pod spec is also documentation-only, like Docker's EXPOSE. Traffic is only routed to a pod when a Service selects it and routes to the specified port. The containerPort field helps humans and tooling understand what's running.
Kubernetes Services
Pods are ephemeral — they die and are replaced with new IPs constantly. A Service gives you a stable virtual IP (ClusterIP) and a DNS name that always points to the healthy pods behind it, regardless of pod restarts or scaling events. kube-proxy maintains iptables/IPVS rules to route traffic to the right pods.
| Service Type | Accessible From | How it Works | Use Case |
|---|---|---|---|
| ClusterIP | Inside cluster only | Virtual IP only routable within the cluster. Default type. | Internal microservice comms (API → DB) |
| NodePort | Outside + Inside | Opens a static port (30000–32767) on every node. Traffic on NodeIP:30080 → pods. |
Dev/test, no cloud LB available |
| LoadBalancer | External internet | Cloud provider provisions an external LB with a public IP. Routes to NodePort → pods. | Production external access |
| ExternalName | Inside cluster only | DNS CNAME alias to an external hostname. No proxy, no pods. | Map external service into cluster DNS |
| Headless | Inside cluster only | ClusterIP: None. DNS returns individual pod IPs (not a VIP). Used with StatefulSets. | Stateful apps: Cassandra, Kafka, MySQL |
CoreDNS & Service Discovery
CoreDNS runs as a Deployment inside Kubernetes and acts as the cluster's DNS server. Every pod is configured (via /etc/resolv.conf) to send DNS queries to the CoreDNS ClusterIP. CoreDNS resolves Service names and returns their ClusterIP addresses.
DNS Name Formats
| What you're querying | Short form (same namespace) | Full FQDN |
|---|---|---|
| Service in same namespace | api-service |
api-service.production.svc.cluster.local |
| Service in different namespace | api-service.production |
api-service.production.svc.cluster.local |
| Headless pod (StatefulSet) | N/A | pod-0.postgres.production.svc.cluster.local |
| External domain (forwarded) | google.com |
Forwarded to upstream DNS resolver |
CNI Plugins
CNI (Container Network Interface) is the plugin standard that Kubernetes uses to set up pod networking. When a pod is created, Kubernetes calls the CNI plugin to assign an IP, configure the veth pair, and set up routing. Without a CNI plugin, pods cannot communicate.
| CNI Plugin | Model | Network Policy | Performance | Best For |
|---|---|---|---|---|
| Flannel | VXLAN overlay | No | Medium | Simplest setup, dev/test clusters |
| Calico | BGP routing or overlay | Yes | High | Production, large clusters, network policies |
| Cilium | eBPF-based | Yes (L7) | Highest | Security-focused, observability, service mesh lite |
| Weave | Overlay mesh | Yes | Medium | Simple multi-cloud / multi-host |
| AWS VPC CNI | Native VPC IPs | Via SGs | High | EKS — pods get real VPC IPs |
Ingress Controllers
An Ingress is a Kubernetes resource that defines HTTP/HTTPS routing rules — which hostnames and URL paths map to which internal Services. An Ingress Controller (nginx, Traefik, HAProxy, or a cloud-native one) is the actual reverse proxy that reads those rules and routes traffic. Think of Ingress as the config and the controller as the engine.
:443
TLS termination
:80
:8080
:3000
Popular Ingress Controllers
Community + official Kubernetes version. Extensive annotation-based configuration. Handles SSL termination, rate limiting, auth, rewrites. Best for teams familiar with nginx config.
Automatic service discovery — reads K8s resources and auto-configures routes. Native Let's Encrypt integration. Built-in dashboard. Popular in homelab and smaller production setups.
Cloud provider manages the load balancer. aws-load-balancer-controller provisions ALBs automatically from Ingress resources. Best for EKS/GKE — no nginx to manage.
Egress & Network Policy
Egress refers to outbound traffic from pods — pods talking to the internet, to external databases, or to other services. By default, Kubernetes allows all egress. Network Policies let you restrict which pods can talk to what, in both directions.
Default Behaviour (No Policies Applied)
- Any pod can reach any other pod in the cluster
- Any pod can reach external internet endpoints
- Any pod can reach the Kubernetes API server
- This is a significant security risk in production
- A compromised pod can pivot to reach all other services
- Default deny-all once ANY policy selects a pod
- Only explicitly listed sources/destinations allowed
- Ingress rules: who can send traffic TO this pod
- Egress rules: where this pod can SEND traffic TO
- CNI plugin must support NetworkPolicy (Calico, Cilium)
kube-system namespace on port 53/UDP as shown above.Frontend ↔ Backend Communication
Let's trace a full request from a user's browser through the cluster to the database and back. This is the pattern you'll implement in almost every production application.
Frontend Served from K8s vs CDN
- React/Vue/Angular built to static files
- Served from CDN (Cloudflare, CloudFront) or nginx pod
- App makes API calls to
api.myapp.com - CORS must be configured on the backend
- Ingress routes
/api/*to backend,/*to frontend
- Dedicated BFF service per client type (web, mobile)
- BFF aggregates calls to multiple microservices
- Browser only talks to BFF (single origin — no CORS)
- BFF handles auth session, token exchange
- Each BFF is a ClusterIP service + Ingress rule
CORS in Kubernetes Context
WebSocket Support
Microservices Communication
In a microservices architecture, services need to talk to each other. There are two broad categories: synchronous (the caller waits for the response) and asynchronous (the caller publishes an event and continues). The right choice depends on your latency, reliability, and coupling requirements.
Synchronous Communication (REST / gRPC)
- Service calls
http://order-svc:8080/v1/orders/123 - K8s Service DNS resolves
order-svcto ClusterIP - Simple, universally understood, easy to debug
- Drawback: tight coupling — if
order-svcis down, caller fails - Implement circuit breakers and retries
- Binary protocol (Protocol Buffers) — 3-5× smaller payload than JSON
- HTTP/2 multiplexing — multiple streams per connection
- Strongly typed contracts via
.protofiles - Requires L7 load balancing for proper round-robin
- Best for: internal service-to-service high-throughput calls
Asynchronous Communication (Event-Driven)
- Producer puts message in queue, continues immediately
- Consumer processes at its own pace
- If consumer is down, messages queue up (not lost)
- One-to-one: each message consumed by exactly one consumer
- Use for: order processing, email sending, file uploads
- Events stored durably on topics (like a distributed log)
- Multiple consumers can read the same event independently
- Replay past events (not possible with queues)
- Consumer groups enable horizontal scaling
- Use for: audit logs, analytics, event sourcing
Microservice Communication Patterns
| Pattern | Protocol | Coupling | Latency | When to Use |
|---|---|---|---|---|
| Direct REST | HTTP/1.1 | Tight | Low | Simple CRUD, reads, user-facing actions |
| gRPC | HTTP/2 + Protobuf | Tight | Very Low | High-throughput internal calls, streaming |
| Message Queue | AMQP / SQS | Loose | Medium | Background jobs, notifications, reliability needed |
| Event Stream | Kafka / Kinesis | Very Loose | Medium | Audit, analytics, fan-out, event sourcing |
| GraphQL | HTTP | Tight | Low | BFF pattern, flexible client queries |
| Service Mesh | mTLS / Envoy | Managed | +~1ms overhead | Security, observability, traffic management |
Service Mesh
A service mesh injects a lightweight proxy sidecar (Envoy) into every pod. All traffic passes through the sidecar — giving you mTLS encryption, retries, circuit breaking, distributed tracing, and traffic splitting without changing a single line of application code.
- Plain HTTP between services (unencrypted)
- No automatic retries on failure
- No circuit breaker — cascading failures
- Tracing requires SDK in every service
- Traffic splitting requires code changes
- No mutual authentication between services
- Automatic mTLS on all service-to-service traffic
- Configurable retry policies (3 retries, 100ms backoff)
- Circuit breaker built into Envoy proxy
- Traces injected at proxy level — zero SDK needed
- Canary: 90% traffic v1, 10% traffic v2 via config
- Service identity via SPIFFE/SVID certificates
Load Balancing Strategies
| Strategy | How it Works | Where it Applies in K8s |
|---|---|---|
| Round Robin | Each request goes to the next server in rotation | kube-proxy default (iptables), gRPC headless services |
| Least Connections | Route to the server with fewest active connections | IPVS mode kube-proxy, nginx upstream |
| IP Hash / Sticky | Hash client IP → always route to same server | Ingress session affinity, stateful WebSockets |
| Weighted | Send X% traffic to v1, Y% to v2 | Istio VirtualService weights, Argo Rollouts |
| Random | Random selection from healthy pods | Envoy/Linkerd proxy default |
| Health-aware | Only route to pods passing readiness probes | K8s Endpoints controller (automatic) |
Network Policies — Zero Trust in K8s
Zero Trust networking in Kubernetes means: no pod is implicitly trusted, every communication is explicitly authorized. Start with a default-deny policy on every namespace, then carve out the minimum required paths.
Monitoring & Troubleshooting
Common Networking Problems & Solutions
| Symptom | Likely Cause | Fix |
|---|---|---|
connection refused on service | No pods selected by service (empty Endpoints) | Check pod labels match service selector |
no such host DNS failure | CoreDNS not running, or DNS egress blocked | Check CoreDNS pods; add DNS egress NetworkPolicy |
Intermittent timeout | NetworkPolicy blocking some pods; one healthy pod, others blocked | Audit NetworkPolicies with kubectl describe |
| Ingress returns 502 | Backend pods not ready; wrong targetPort | Check pod readinessProbe; verify port mapping |
| TLS cert not working | cert-manager not provisioning; wrong secret name | Check cert-manager logs; kubectl describe certificate |
| gRPC load balancing broken | Single long-lived HTTP/2 connection hits one pod | Use headless service + client-side LB or service mesh |
| External IP pending forever | Cloud LB controller not installed / configured | Check cloud-controller-manager; verify IAM permissions |
| Pod can't reach internet | Egress NetworkPolicy blocking all external traffic | Add explicit egress rule for external IP ranges |
Network Security
- Use cert-manager + Let's Encrypt for automatic TLS on Ingress
- Enable mTLS between services (via service mesh or manual)
- Never expose internal services without TLS in production
- Rotate certificates automatically (cert-manager handles this)
- Enforce TLS 1.2+ minimum; disable TLS 1.0/1.1
- Never expose kube-apiserver (port 6443) to the internet
- Use a VPN or bastion host for cluster access
- Enable audit logging on the API server
- Use RBAC — no wildcards in production ClusterRoles
- Restrict access with
--anonymous-auth=false
- Set
automountServiceAccountToken: falseif not needed - Use read-only root filesystem where possible
- Don't run containers as root (
runAsNonRoot: true) - Drop all Linux capabilities, add back only required ones
- Use seccomp and AppArmor profiles
- Rate limiting on all public-facing Ingress resources
- WAF (Web Application Firewall) in front of Ingress
- Use
allowPrivilegeEscalation: falseon ingress pods - Block access to internal metadata endpoints (
169.254.169.254) - Implement CORS headers — never use wildcard
*in production
Quick Reference
K8s DNS Cheat Sheet
kubernetes.io/docs/concepts/services-networking/ · Cilium Network Policy Editor (editor.cilium.io) · Cloudflare Learning — How Does DNS Work.