Kubernetes NetworkPolicies are most useful when they are introduced as a small, testable access model rather than a collection of copied YAML files. This guide explains how policy selection and ingress or egress rules behave, provides reusable patterns for common application tiers, and gives you a practical checklist for testing DNS, ingress controllers, service dependencies, and external traffic before enforcing zero-trust pod communication.
Overview
A Kubernetes NetworkPolicy controls which network connections are allowed to or from pods selected by the policy. The Kubernetes API defines the policy object, but enforcement is provided by the cluster's network plugin or CNI. A policy can therefore be syntactically valid and still require verification that the installed CNI supports and enforces the features you depend on.
Policies are additive. If several policies select the same pod, the allowed connections from all applicable policies are combined. A policy does not provide a deny rule that overrides every other policy. Instead, isolation begins for a direction when at least one policy selects a pod for that direction. Once ingress or egress is isolated, only traffic allowed by the applicable policies is permitted for that direction.
| Policy behavior | Practical meaning |
|---|---|
| No policy selects the pod | That direction remains non-isolated under standard NetworkPolicy behavior. |
A policy selects the pod with policyTypes: [Ingress] | Ingress becomes isolated; only listed ingress sources and ports are allowed. |
A policy selects the pod with policyTypes: [Egress] | Egress becomes isolated; only listed destinations and ports are allowed. |
An empty ingress: [] or egress: [] | That direction is explicitly denied for the selected pods, subject to the capabilities and behavior of the CNI. |
| Multiple rules or policies | Allowed sources and destinations are combined; entries within a list are generally alternatives, not cumulative requirements. |
NetworkPolicy is not an identity-aware application firewall. Standard policy can select pods and namespaces with labels, and can allow CIDR ranges and ports, but it does not directly select a Kubernetes service account. If access must follow an application identity, use carefully managed pod labels or a networking solution with additional identity-aware controls.
Checklist by scenario
Start with a default-deny baseline
Apply restrictive policies in a test namespace first. A common baseline selects all pods and isolates both directions:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
This is a starting point, not a complete production policy. It can block DNS, health checks, telemetry, admission-related callbacks, and application dependencies unless those paths are explicitly allowed.
Allow frontend-to-backend traffic
Use namespace and pod labels to describe the intended relationship. When both namespaceSelector and podSelector appear in the same peer entry, they describe an AND condition: the source pod must have the pod label and run in the selected namespace.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: payments
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: web
podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
Confirm that the namespace label exists and that the application actually listens on the declared port. A Service port and a container port may have different numbers, so test the destination port seen by the pod.
Allow egress to DNS
After egress isolation, name resolution commonly fails unless DNS is allowed. The exact namespace and labels for the DNS pods vary by cluster, so inspect them rather than assuming a fixed location.
kubectl get pods -A -l k8s-app=kube-dns --show-labels
kubectl get svc -A | grep -i dns
Then create an egress rule for the discovered DNS destination and allow both UDP and TCP port 53. TCP can be needed when a DNS exchange does not fit in a UDP response or when a resolver falls back to TCP.
Allow ingress from an ingress controller
Identify where the ingress controller runs and which labels its pods carry. Allow traffic from those controller pods to the application port. Traffic may be observed as coming from the controller, a node, or another address depending on the controller, service configuration, and networking implementation. Validate the actual source path in your cluster before narrowing the rule.
Allow an external dependency
For databases, APIs, or managed services outside the cluster, document the destination hostname, resolved address ranges, protocol, and port. CIDR-based rules can be brittle when a provider changes addresses. Where supported by your networking platform, use an appropriate egress gateway, stable endpoint, or dedicated control that makes the dependency boundary explicit.
What to double-check
- Labels: Check namespace and pod labels with
kubectl get namespace --show-labelsandkubectl get pods -A --show-labels. A selector that matches nothing may create false confidence. - DNS: Test both service-name and external-name resolution from a restricted pod. Confirm the resolver IP, protocol, and port used by the workload.
- Ingress controllers: Determine whether the application receives traffic from controller pods, node addresses, a load balancer, or another proxy layer.
- Service accounts: Do not assume a service account is selectable by standard NetworkPolicy. If identity matters, define and govern a reliable label or use an additional security control.
- Health checks and telemetry: Include kubelet or load-balancer probes, metrics agents, log collectors, tracing exporters, and policy or admission components where applicable.
- Test from the right location: A successful request from an administrator's laptop does not prove that the selected pod can reach the same destination.
- CNI behavior: Review the installed plugin's documentation and test the features you use, especially host networking, named ports, IP blocks, and cross-node traffic.
A practical test loop is to inspect the rendered policy, apply it to a disposable namespace, and launch a temporary diagnostic pod with tools appropriate for your image standards. For example:
kubectl apply --dry-run=server -f policy.yaml
kubectl -n payments run netcheck --rm -it --restart=Never --image=busybox:stable -- sh
# Inside the temporary pod:
nslookup api.payments.svc.cluster.local
wget -S -O- http://api:8080/health
nc -vz api 8080
Use an approved image rather than copying this image reference blindly. Test permitted and forbidden paths, pod-to-pod traffic across nodes, DNS, readiness checks, and each declared external dependency. Capture the policy version and test result alongside the deployment change.
Common mistakes
- Applying a default-deny egress policy without first allowing DNS.
- Using a namespace selector alone when the intent is to allow only one application.
- Confusing a Service name or port with the actual destination port used by NetworkPolicy evaluation.
- Assuming an empty policy rule means “allow everything.” An empty peer list is not the same as omitting isolation.
- Relying on IP addresses that change while the policy is treated as permanent documentation.
- Testing only a successful path and not verifying that an unrelated namespace or pod is blocked.
- Editing live policies manually without keeping the source manifest in version control and assigning ownership.
For broader operational context, pair this review with a Kubernetes troubleshooting decision tree and the Kubernetes upgrade checklist. Network behavior can change during platform upgrades, CNI changes, or workload migrations.
When to revisit
Revisit NetworkPolicies before seasonal planning cycles, during recurring access reviews, and whenever workflows or tools change. The most important triggers are a new namespace, service, ingress controller, database, observability agent, DNS design, or external API. Also review policies before a Kubernetes version or CNI upgrade, after changing service-account or labeling conventions, and after an incident involving unexpected reachability or blocked traffic.
Keep a short inventory for every policy: owner, selected pods, intended callers, permitted ports, DNS dependency, external dependencies, test command, and last review date. At the next review, compare that inventory with live labels and deployed services, run positive and negative connectivity tests, and remove exceptions that no longer have an owner. If a policy change affects a critical path, stage it in a representative namespace, monitor application and SLO signals, and retain a rollback manifest. This makes zero-trust pod communication a maintained platform capability rather than a one-time YAML exercise.
For related operational planning, see the SLO and error budget template and the internal developer platform roadmap.