Kubernetes Network Policies: A Practical Tutorial
Learn how Kubernetes NetworkPolicies work, how to lock down pod-to-pod traffic with selectors, and how to roll out a default-deny posture without breaking your cluster.
What you'll learn
- ✓What NetworkPolicies actually control
- ✓How ingress and egress rules are evaluated
- ✓How to apply a default-deny posture safely
- ✓Common selector mistakes that cause outages
Prerequisites
- •Basic Kubernetes pod and namespace knowledge
What and Why
By default, every pod in a Kubernetes cluster can talk to every other pod across every namespace. That is fine for a demo, but it is a disaster for compliance and blast radius. A compromised frontend should not be able to query the payments database directly. NetworkPolicies are namespaced resources that tell the CNI plugin which traffic is allowed in and out of pods that match a label selector.
NetworkPolicies are enforced by the CNI (Calico, Cilium, Antrea). If your CNI does not support them, the resource is silently ignored, which is an unpleasant surprise to discover during an audit.
Mental Model
Think of NetworkPolicies as a stateful allow-list firewall attached to pods, not to namespaces. As soon as a pod is matched by at least one policy on a given direction (ingress or egress), all traffic in that direction is denied except what the policies explicitly allow. If no policy selects the pod for that direction, the pod is wide open.
That asymmetry trips people up. Creating an ingress policy does not affect egress, and vice versa.
Hands-on Example
Let us protect a payments API so only the checkout service in the web namespace can reach it.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: payments-allow-checkout
namespace: payments
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: web
podSelector:
matchLabels:
app: checkout
ports:
- protocol: TCP
port: 8080
The combined namespaceSelector and podSelector under the same from entry is an AND. If you wrote them as two list items, it would be an OR, which is much more permissive than most people intend.
namespace: web namespace: payments
[checkout pod] ---allowed---> [payments-api:8080]
[other pod] ---denied----X [payments-api:8080]
namespace: analytics
[any pod] ---denied----X [payments-api:8080] Next, add a default-deny baseline in the payments namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
An empty podSelector selects every pod in the namespace. With no ingress rules listed, all inbound traffic is dropped except what other policies explicitly permit.
Common Pitfalls
The biggest mistake is forgetting that DNS is egress traffic too. If you apply a default-deny egress policy, pods cannot resolve kubernetes.default or any external host until you allow egress to kube-dns on UDP and TCP 53.
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
Other gotchas: NetworkPolicies do not apply to host-networked pods, they cannot match by IP for in-cluster traffic reliably across CNIs, and the kubernetes.io/metadata.name label only exists on namespaces in 1.22 and later.
Production Tips
Roll out policies in three phases. First, deploy them in audit or log-only mode if your CNI supports it (Calico has Log, Cilium has Hubble). Second, apply targeted allow rules for known flows. Third, add the default-deny last, after you have watched logs for at least one full business cycle.
Label every namespace with name: <namespace> at creation time so cross-namespace selectors are easy. Keep one policy per logical flow rather than one giant policy with many rules; small policies are easier to review and delete.
Pair NetworkPolicies with PodSecurity admission and a service mesh for mTLS. NetworkPolicies answer “who can connect”, not “is the connection encrypted and authenticated”.
Wrap-up
NetworkPolicies are the cheapest, most effective microsegmentation tool in Kubernetes. Start by labeling everything, add a default-deny per namespace, then carve allow-lists for real flows. The result is a cluster where a single compromised pod cannot pivot freely, and your auditors stop asking uncomfortable questions.
Related articles
- Kubernetes Kubernetes ConfigMaps and Secrets Tutorial
A practical walkthrough of ConfigMaps and Secrets in Kubernetes, including how to inject them as environment variables, mount as files, and rotate safely.
- Kubernetes Kubernetes Ingress vs LoadBalancer vs NodePort Explained
Understand the three ways to expose a Kubernetes service to the outside world, the tradeoffs of each, and how to pick the right one for your workload.
- Kubernetes Kubernetes Networking: Services, kube-proxy, and CNI Plugins
How traffic actually flows in a Kubernetes cluster: the pod network, services, kube-proxy modes, and what a CNI plugin really does.
- Kubernetes Kubernetes RBAC Cheatsheet: Roles, Bindings, and Service Accounts
A concise tour of Kubernetes RBAC: roles vs cluster roles, bindings, service accounts, and patterns that scale without becoming a permissions zoo.