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 28

curl'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: Ingress

describe 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: Ingress

Now 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: Egress

With 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
catalog

Work 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 from or to item? 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.

Start Wrong door

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 podSelector really 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 list Ingress, Egress or both under policyTypes with 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 exec rather than through a forward.

AboutGuidesCompare

PrivacyCookiesTerms

Hackademy is a trading name of Mather Software Ltd, registered in England and Wales, company number 17443861. ICO registration ZC256628.

Contact: support@mathersoftware.com© 2026 Mather Software Ltd