Kubernetes NetworkPolicy Cheatsheet: 12 Copy-Paste Patterns
NetworkPolicies are powerful but confusing. 12 production-ready YAML patterns for ingress, egress, namespace isolation and monitoring. Including the AND-vs-OR trap.
TL;DR - NetworkPolicies are Kubernetes-native firewall rules at pod level. Without them all traffic is allowed. 12 copy-paste patterns cover 90% of production cases: from deny-all defaults to Prometheus scraping and DNS egress. The biggest trap is the AND-vs-OR semantics on selectors.
🔖 Just want the YAMLs? Here’s the interactive NetworkPolicy cheatsheet - with a copy button per pattern and common mistakes. Bookmark recommended.
How NetworkPolicies work
The mental model in three sentences:
- Without a policy, everything is allowed. Any pod can talk to any pod in any namespace. That’s the Kubernetes default.
- Once a policy matches a pod, everything not explicitly allowed is blocked. A
podSelector: {}matches all pods in the namespace. - Policies are additive. Multiple policies on the same pod never contradict, they combine their allows.
Important: NetworkPolicies need a CNI plugin that enforces them. Cilium, Calico and Weave Net do. Default kubenet in some managed K8s setups silently ignores policies. That’s the sneakiest trap: you write policies, test on the cluster, everything still works, and you think “policy is active”. In reality nothing is enforced.
# Check CNI
kubectl get pods -n kube-system -l k8s-app=cilium
kubectl get pods -n kube-system -l k8s-app=calico-node
The AND-vs-OR trap
The most common NetworkPolicy confusion. Two selectors in a from: block behave differently depending on formatting:
OR (two separate list items):
ingress:
- from:
- namespaceSelector: # list item 1
matchLabels:
name: monitoring
- podSelector: # list item 2
matchLabels:
app: prometheus
Means: traffic from all pods in namespace “monitoring” OR from all pods with label app=prometheus in the own namespace. Much more open than intended.
AND (one single list item):
ingress:
- from:
- namespaceSelector: # part of list item 1
matchLabels:
name: monitoring
podSelector: # also part of list item 1
matchLabels:
app: prometheus
Means: traffic only from pods with label app=prometheus that are in namespace “monitoring”. That’s usually what you want.
The difference is two characters in the YAML: the dash before podSelector. With dash: OR. Without: AND.
The 12 patterns
Deny defaults (the foundation)
Every zero-trust approach starts with a deny-all. Block everything first, then explicitly allow.
Pattern 1: Deny all ingress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-ingress
namespace: <ns>
spec:
podSelector: {}
policyTypes:
- Ingress
Blocks all incoming traffic to all pods in the namespace. No ingress: block = no exceptions.
Pattern 2: Deny all egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all-egress
namespace: <ns>
spec:
podSelector: {}
policyTypes:
- Egress
Blocks all outgoing traffic. Caution: DNS is also blocked. Pods can’t resolve service names. Always combine with pattern 10 (DNS egress).
Pattern 3: Deny all (both)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: <ns>
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
Complete isolation. Namespace is silent. Open patterns one by one from here.
Namespace and pod access
Pattern 4: Allow from same namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-same-namespace
namespace: <ns>
spec:
podSelector: {}
ingress:
- from:
- podSelector: {}
policyTypes:
- Ingress
All pods in the namespace can reach each other, but nothing from outside. The most common pattern for internal microservices.
Pattern 5: Allow from specific namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-namespace
namespace: <ns>
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: backend
policyTypes:
- Ingress
Allows traffic from a specific namespace (here: backend). Prerequisite: the source namespace must have a matching label (kubectl label namespace backend name=backend).
Pattern 6: Allow specific port only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-http-only
namespace: <ns>
spec:
podSelector:
matchLabels:
app: web
ingress:
- ports:
- port: 8080
protocol: TCP
from:
- podSelector: {}
policyTypes:
- Ingress
Only port 8080/TCP, only from the own namespace. Everything else blocked.
Kubernetes Debugging - systematic, not guesswork
Replay real production incidents, internalise kubectl workflows, find root causes in minutes.
View workshop detailsInfrastructure access
Pattern 7: Allow from ingress controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress
namespace: <ns>
spec:
podSelector:
matchLabels:
app: web
ingress:
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx
policyTypes:
- Ingress
Allows inbound traffic from the ingress controller namespace. Without this pattern your app is unreachable from outside, even if the Ingress is correctly configured. Common labels: ingress-nginx, traefik, istio-ingress.
Pattern 8: Allow Prometheus scraping (AND variant)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-prometheus
namespace: <ns>
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app.kubernetes.io/name: prometheus
ports:
- port: 9090
protocol: TCP
policyTypes:
- Ingress
AND variant: only Prometheus (label app.kubernetes.io/name: prometheus) from the monitoring namespace. Adjust the port to your app’s metrics port.
Pattern 9: Allow from specific pod (database access)
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-backend-to-db
namespace: <ns>
spec:
podSelector:
matchLabels:
app: database
ingress:
- from:
- podSelector:
matchLabels:
app: backend
ports:
- port: 5432
protocol: TCP
policyTypes:
- Ingress
Only the backend pod may access the database, only on port 5432.
Egress control
Pattern 10: Allow DNS egress only
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns-egress
namespace: <ns>
spec:
podSelector: {}
egress:
- ports:
- port: 53
protocol: UDP
- port: 53
protocol: TCP
policyTypes:
- Egress
The most important egress pattern. Without DNS egress, pods can’t resolve service names: curl http://api-service:8080 fails even though the pod is technically reachable. Always combine with deny-all-egress (pattern 2 or 3).
Pattern 11: Allow egress to external CIDR
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-egress-to-db
namespace: <ns>
spec:
podSelector:
matchLabels:
app: backend
egress:
- to:
- ipBlock:
cidr: 10.0.0.0/24
ports:
- port: 5432
protocol: TCP
- ports:
- port: 53
protocol: UDP
policyTypes:
- Egress
Backend may only reach an external database (subnet 10.0.0.0/24, port 5432) and DNS. For RDS, Cloud SQL, or managed DB instances outside the cluster.
Pattern 12: Allow egress to internal service
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: <ns>
spec:
podSelector:
matchLabels:
app: frontend
egress:
- to:
- podSelector:
matchLabels:
app: api
ports:
- port: 8080
protocol: TCP
- ports:
- port: 53
protocol: UDP
policyTypes:
- Egress
Frontend may only talk to the API service (port 8080) and DNS.
The 5 most common mistakes
- Forgetting DNS egress. Deny-all-egress without DNS allow (pattern 10) kills all connections, even though ingress policies are correct.
- Confusing AND and OR. One dash difference in the YAML opens access far wider than intended. Always test explicitly.
- Missing namespace labels.
namespaceSelectormatches on namespace labels, not on the name. Set labels manually:kubectl label namespace <ns> name=<ns>. - CNI doesn’t enforce. Policies exist in the API but kubenet ignores them.
kubectl get pods -n kube-system | grep ciliumto verify. - Policies only on ingress, forgot egress. Ingress policies alone don’t control outgoing traffic. Zero-trust needs both.
Where to go from here
In the Kubernetes Debugging Workshop we simulate an incident where a missing NetworkPolicy causes unintended cross-namespace traffic, and debug it systematically.
Related from our series:
- kubectl Debugging Cheatsheet - 12 commands for any pod issue
- Pod Pending: 23 causes - when pods won’t start
- OOMKilled: 6 causes - when pods die
- NetworkPolicy cheatsheet (interactive) - all 12 patterns with copy buttons and common mistakes
Kubernetes Debugging - systematic, not guesswork
Replay real production incidents, internalise kubectl workflows, find root causes in minutes.
View workshop detailsKeep reading
kubectl Debugging Cheatsheet: 12 Commands for Production Incidents
Structured debugging workflow for Kubernetes in production: 12 kubectl commands in the right order - from pod status to ephemeral debug containers.
8 min
Pod Pending in Kubernetes: 23 Causes, Decision Tree, Fix Workflow
Pod stuck in Pending? The 23 most common causes across 5 categories, with kubectl commands, a decision tree and a workflow to diagnose in under 5 minutes.
10 minNeed a second opinion on your cluster?
Book a free 30-minute Kubernetes health check. We review your setup and give concrete recommendations, no sales pitch.
Book a slot