Debugging NetworkPolicy: why pods can't reach each other
By Hackademy. Updated . Output from a real Kubernetes v1.35 cluster.
The short answer
When pods cannot reach each other after a NetworkPolicy is applied, the policy is usually doing exactly what it says. Once any policy selects a pod for ingress or egress, only traffic that some policy allows in that direction gets through, and everything else is dropped silently, so connections hang and time out instead of being refused. The usual causes are a policy that selects the pod but does not list the caller, a podSelector that only matches pods in the policy's own namespace, a rule on the Service's port instead of the pod's port, and an egress policy that does not allow DNS to kube-dns on port 53. To diagnose it, run kubectl get networkpolicy to find the policies, kubectl describe networkpolicy NAME to read what each allows, and test from the calling pod with kubectl exec POD -- curl -m 5 SERVICE. Fix it by adding the missing peer, namespace, port or DNS rule, and check that your network plugin enforces NetworkPolicy at all.
Recognise a connection a policy drops
A NetworkPolicy does not reject traffic, it drops it. The caller gets no error back, only silence until something times out. Always test with a timeout, or you can wait minutes for an answer. Without one, this curl took over two minutes to give up:
$ kubectl exec deploy/cart -- curl -sS http://catalog
curl: (28) Failed to connect to catalog port 80 after 132591 ms: Couldn't connect to server
command terminated with exit code 28$ kubectl exec deploy/frontend -- curl -sS --max-time 3 http://catalog
curl: (28) Connection timed out after 3001 milliseconds
command terminated with exit code 28curl's exit code 28 is a timeout. Compare it with Connection refused, which means the packets arrived and nothing was listening on that port: a Service pointing at the wrong targetPort, or an application that has not started. A refusal is not a policy problem.
Find the policies that select the pod
Policies are namespaced and select pods by label. List them in the destination's namespace (for ingress) and the caller's namespace (for egress), and look at POD-SELECTOR:
$ kubectl get networkpolicy
NAME POD-SELECTOR AGE
catalog-deny-all app=catalog 1s$ kubectl describe networkpolicy catalog-deny-all
Name: catalog-deny-all
Namespace: default
Created on: 2026-09-29 21:02:30 +0100 BST
Labels: <none>
Annotations: <none>
Spec:
PodSelector: app=catalog
Allowing ingress traffic:
<none> (Selected pods are isolated for ingress connectivity)
Not affecting egress traffic
Policy Types: Ingressdescribe says it plainly: catalog pods are isolated for ingress, and nothing is allowed in. Policies only ever add permissions, and there is no deny rule. A pod's allowed traffic is the union of every policy that selects it, so the fix is never to edit the deny-all, but to add a policy that allows the caller:
$ kubectl describe netpol catalog-allow-frontend
Name: catalog-allow-frontend
Namespace: default
Created on: 2026-09-29 21:02:39 +0100 BST
Labels: <none>
Annotations: <none>
Spec:
PodSelector: app=catalog
Allowing ingress traffic:
To Port: 80/TCP
From:
PodSelector: app=frontend
Not affecting egress traffic
Policy Types: IngressNow the frontend gets through and every other pod, such as the cart, still times out. That is a correct policy. If the caller is in another namespace, such as an ingress controller in ingress-nginx, a podSelector alone will not match it: add a namespaceSelector, using the kubernetes.io/metadata.name label that every namespace carries.
Check the ports and DNS in egress rules
Egress policies cause the most confusing failures, because they also govern DNS. Once an egress policy selects a pod, it can no longer ask kube-dns for names unless a rule allows it, so every request fails while resolving, before any connection is tried:
$ kubectl describe networkpolicy frontend-egress
Name: frontend-egress
Namespace: default
Created on: 2026-10-05 11:11:05 +0000 UTC
Labels: <none>
Annotations: <none>
Spec:
PodSelector: app=frontend
Not affecting ingress traffic
Allowing egress traffic:
<none> (Selected pods are isolated for egress connectivity)
Policy Types: EgressWith this policy, curl -m 5 catalog from a frontend pod fails with Resolving timed out. Allow UDP and TCP port 53 to the kube-dns pods in kube-system first. The second trap is the port: a policy sees traffic after the Service has translated it, so it must allow the pod's targetPort, not the Service's port. In this shop, catalog's Service listens on 80 but its pods on 8080, so the working policy names 8080:
$ kubectl describe networkpolicy frontend-egress
Allowing egress traffic:
To Port: 8080/TCP
To:
PodSelector: app=catalog
----------
To Port: 53/UDP
To Port: 53/TCP
To:
NamespaceSelector: kubernetes.io/metadata.name=kube-system
PodSelector: k8s-app=kube-dns
Policy Types: Egress$ kubectl exec deploy/frontend -- curl -s -m 5 catalog
catalogWork through a checklist
- Does the network plugin enforce NetworkPolicy? Without one that does, policies are accepted and quietly ignored.
- Which policies select the destination for ingress, and which select the caller for egress? Both sides must allow the traffic.
- Do the labels in each selector match the pods' real labels? Check with
kubectl get pods --show-labels. - Is the caller in another namespace? Then the peer needs a
namespaceSelector. - Are two selectors in the same
fromortoitem? There they are combined with AND; as separate list items they are combined with OR. - Is the port the pod's port, and is DNS allowed for any pod with an egress policy?
The missions below build both kinds of policy on a small shop of three services and check the result from inside the cluster, so you can see a dropped connection, a missing DNS rule and a wrong port for yourself without installing anything.
Practise it on a simulated cluster
These missions in the Kubernetes Simulator break a cluster in this way and leave you to fix it. Free in your browser, nothing to install.
- Wrong doorMicroservices shop, intermediate, about 8 min
- Need to knowMicroservices shop, advanced, about 12 min
- Outbound onlyMicroservices shop, advanced, about 15 min
Questions
- Why does my NetworkPolicy have no effect?
Kubernetes stores NetworkPolicy objects, but the network plugin enforces them. If your cluster's plugin does not support NetworkPolicy, every policy is accepted and ignored. Also check that the policy's
podSelectorreally matches the pods you meant, and that it is in their namespace.- How do I write a default deny policy?
Create a policy with an empty
podSelector: {}, which selects every pod in the namespace, and listIngress,Egressor both underpolicyTypeswith no rules. Every pod in the namespace is then isolated in those directions until other policies allow specific traffic. If you deny egress, allow DNS straight away.- Do NetworkPolicies block traffic from the node or from kubectl port-forward?
Traffic from the pod's own node, such as the kubelet's health probes, is always allowed. Port-forwarding is also handled through the node, and how a plugin treats it can vary, so test policies from a real pod with
kubectl execrather than through a forward.