Kubernetes NetworkPolicy Guide
Secure pod-to-pod traffic with Kubernetes NetworkPolicies. Ingress and egress rules, namespace selectors, deny-all policies, and CNI requirements.
π‘ Quick Answer: NetworkPolicies are Kubernetes-native firewall rules that control pod-to-pod and pod-to-external traffic. Start with a deny-all policy per namespace, then explicitly allow required traffic. Requires a CNI that supports NetworkPolicy (Calico, Cilium, Antrea) β default kubenet does NOT enforce them.
The Problem
By default, every pod can communicate with every other pod in the cluster β no segmentation. This means:
- Compromised pod can reach databases directly
- No tenant isolation in multi-tenant clusters
- Lateral movement after initial breach is unrestricted
- Compliance requirements (PCI-DSS, HIPAA) unmet
The Solution
Deny All (Default Stance)
# Deny all ingress to namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: production
spec:
podSelector: {} # Applies to ALL pods in namespace
policyTypes:
- Ingress
---
# Deny all egress from namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- EgressAllow Specific Ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api-server # Target: api-server pods
policyTypes:
- Ingress
ingress:
- from:
# Allow from frontend pods
- podSelector:
matchLabels:
app: frontend
# Allow from monitoring namespace
- namespaceSelector:
matchLabels:
purpose: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 8080Allow DNS Egress (Essential)
# Must allow DNS for any egress-restricted namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53Allow Egress to External
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api
namespace: production
spec:
podSelector:
matchLabels:
app: api-server
policyTypes:
- Egress
egress:
# Allow to external API
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443
# Allow to database
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432Complete 3-Tier Application
# Frontend: accept from ingress controller only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: frontend-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: frontend
ingress:
- from:
- namespaceSelector:
matchLabels:
app: ingress-nginx
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: api
ports:
- port: 8080
---
# API: accept from frontend only, talk to database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: api
ingress:
- from:
- podSelector:
matchLabels:
tier: frontend
ports:
- port: 8080
egress:
- to:
- podSelector:
matchLabels:
tier: database
ports:
- port: 5432
---
# Database: accept from API only, no egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: database-policy
namespace: production
spec:
podSelector:
matchLabels:
tier: database
ingress:
- from:
- podSelector:
matchLabels:
tier: api
ports:
- port: 5432
policyTypes:
- Ingress
- Egress # Empty egress = deny all outboundgraph LR
I[Ingress Controller] --> |:8080| F[Frontend<br/>tier: frontend]
F --> |:8080| A[API<br/>tier: api]
A --> |:5432| D[Database<br/>tier: database]
F -.-> |β blocked| D
I -.-> |β blocked| D
style F fill:#2196F3,color:white
style A fill:#FF9800,color:white
style D fill:#4CAF50,color:whiteCommon Issues
NetworkPolicy not enforced
Your CNI doesnβt support NetworkPolicy. Default kubenet and Flannel do NOT enforce them. Use Calico, Cilium, or Antrea.
Pods canβt resolve DNS after deny-all egress
You need an explicit DNS allow rule. Egress deny-all blocks DNS (UDP/TCP 53) β add the DNS egress policy above.
AND vs OR confusion in rules
Multiple items in the same from entry are ANDβd. Separate from entries are ORβd. This is the #1 NetworkPolicy mistake.
Best Practices
- Deny-all first, allow explicitly β zero-trust networking
- Always allow DNS when restricting egress β everything breaks without it
- Use namespace selectors for cross-namespace rules
- Test with
kubectl execandcurlβ verify connectivity before and after - Use Calico or Cilium for enforcement β kubenet ignores policies silently
- Label namespaces for selector targeting β makes policies more readable
Key Takeaways
- NetworkPolicies are additive β if no policy selects a pod, all traffic is allowed
- Once ANY policy selects a pod, only explicitly allowed traffic gets through
- Requires a compatible CNI (Calico, Cilium, Antrea) β kubenet does NOT enforce
- Always allow DNS egress when using deny-all egress policies
- Multiple
from/toentries are ORβd; multiple selectors in ONE entry are ANDβd - Start with deny-all per namespace, then build allow rules incrementally

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
