Kubernetes NetworkPolicies help control which pods can communicate with other pods and external services. For example, you may want your frontend pods to access an API, allow the API to connect to a database, and block all other traffic.
However, creating a NetworkPolicy does not guarantee that traffic will be restricted as expected. A policy may select the wrong pods, use incorrect labels, omit a required port, or depend on a network plugin that does not enforce NetworkPolicy rules.
The result can be confusing. A request that should work gets blocked, or traffic that should be denied continues to pass.
This article explains how to troubleshoot Kubernetes NetworkPolicies step by step, using kubectl commands and practical YAML examples.
1. Confirm That Your Network Plugin Supports NetworkPolicies
The first thing to check is whether your Kubernetes networking implementation supports NetworkPolicy enforcement.
Kubernetes accepts NetworkPolicy resources through its API, but the API server does not enforce the rules itself. Enforcement is handled by the cluster's network plugin, also called the Container Network Interface (CNI) implementation.
If your networking setup does not support NetworkPolicy enforcement, you may be able to create a policy successfully without seeing any change in network traffic.
Check which networking components are running in your cluster:
kubectl get pods -A
Look for networking components associated with your cluster, such as Calico, Cilium, or another supported implementation. The exact component and namespace depend on how the cluster was installed.
Also check the cluster provider's documentation to confirm whether its networking configuration supports NetworkPolicy enforcement.
Do not assume that a policy is active just because Kubernetes accepts it. Confirm that the installed networking implementation supports the features you are using and that its enforcement components are healthy.
2. Check Whether the Policy Selects the Correct Pods
A NetworkPolicy only applies to the pods selected by its podSelector within the policy's namespace.
Consider a policy that selects API pods:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-traffic
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
This policy allows selected frontend pods to connect to selected API pods on TCP port 8080, subject to the cluster's NetworkPolicy enforcement and the matching traffic path.
The important detail is the label selector:
podSelector:
matchLabels:
app: api
If the actual API pods use a different label, the policy will not select them.
Check the labels on the running pods:
kubectl get pods -n production --show-labels
Inspect a specific pod when necessary:
kubectl get pod api-7d9c8f6b7f-abc12 \
-n production \
--show-labels
You can also list only the pods matching the expected label:
kubectl get pods -n production -l app=api
If the command returns no pods, verify the label name and value in the Deployment or other workload configuration.
A common mistake is selecting app: api when the workload actually uses app: backend-api. Another is adding a policy before checking whether its labels match the deployed resources.
3. Remember That NetworkPolicies Are Namespace-Scoped
NetworkPolicies operate within a namespace. A policy's podSelector selects pods in that namespace, not pods across the entire cluster.
This matters when your frontend and API run in different namespaces.
For example, the API may be in the production namespace while the frontend runs in the web namespace. A policy in production that selects app: frontend using a podSelector alone will look for matching pods in production, not web.
To select pods from another namespace, combine a namespace selector with a pod selector:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
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
Here, the API pods are selected in production. The allowed source pods must have the app: frontend label and belong to a namespace labeled kubernetes.io/metadata.name: web.
The namespace label shown is commonly provided by Kubernetes, but verify it in your cluster:
kubectl get namespace web --show-labels
Pay attention to YAML indentation. In the example, namespaceSelector and podSelector are part of the same source entry. This means both conditions must match.
If they are accidentally placed in separate list entries, the policy can allow traffic from a broader set of sources than intended.
4. Understand Ingress, Egress, and Policy Isolation
NetworkPolicies control two traffic directions:
A policy does not automatically restrict both directions. The directions selected by policyTypes determine which traffic is isolated by that policy.
For example:
policyTypes:
- Ingress
This policy concerns inbound traffic to selected pods. It does not, by itself, isolate their outbound traffic.
If you want to restrict both directions, declare both:
policyTypes:
- Ingress
- Egress
This distinction often explains why a connection fails even though one side appears to have the correct rule.
Suppose an API pod needs to connect to a database. The API pod may need an egress rule permitting the database connection. The database pod may also need an ingress rule allowing traffic from the API.
Both directions must permit the connection when both endpoints are isolated for the relevant direction.
5. Check for Other Policies That Also Apply
Kubernetes NetworkPolicy rules are additive. There is no general priority order in which the newest policy overrides older policies.
For a given direction, if a pod is selected by one or more policies that isolate that direction, the traffic allowed by those policies is combined. A matching allow rule in another applicable policy can permit traffic even when a different policy does not allow it.
This means reviewing only one policy may not explain the observed behavior.
List all policies in the namespace:
kubectl get networkpolicy -n production
Inspect each relevant policy:
kubectl describe networkpolicy -n production
For a particular policy, use:
kubectl describe networkpolicy allow-api-traffic \
-n production
Look for policies that select the same pods, particularly broad policies that permit traffic from many sources.
Also check whether a default-deny policy has been configured. A policy with an empty podSelector selects all pods in its namespace:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
This isolates all pods in production for ingress, except for traffic permitted by applicable NetworkPolicies.
A default-deny policy is useful for building a restricted environment, but required traffic must then be explicitly allowed. If an application suddenly stops communicating after such a policy is introduced, check which connections have not yet been permitted.
6. Verify the Destination Port
Another frequent issue is allowing the wrong port.
A NetworkPolicy port refers to the port used by the selected pod for the connection. It is not necessarily the external port exposed by a Service.
Consider this Service:
apiVersion: v1
kind: Service
metadata:
name: api
namespace: production
spec:
selector:
app: api
ports:
- port: 80
targetPort: 8080
Clients connect to the Service on port 80, but the Service forwards traffic to port 8080 on the selected API pods.
A NetworkPolicy allowing traffic to API pods on TCP port 80 may therefore be incorrect for this setup. The policy would normally need to permit the pod's destination port, 8080.
Check the Service and its endpoints:
kubectl get service api -n production -o yaml
kubectl get endpointslice -n production \
-l kubernetes.io/service-name=api
Also verify that the application is actually listening on the expected port.
If the application listens on port 8080 but the policy allows only 8081, the connection may fail even when the selectors are correct.
7. Test Traffic From the Actual Application Pod
Testing from your laptop or a separate debugging pod does not always reproduce the application's network path.
Start by checking the application pod:
kubectl get pods -n production -o wide
If the container includes a suitable client, test the destination from that pod:
kubectl exec -n production api-7d9c8f6b7f-abc12 \
-- curl -v --connect-timeout 5 http://database:8080
Replace the pod name, hostname, and port with values from your environment. The example assumes that curl is installed in the container and that the destination exposes HTTP; a database connection needs a suitable client instead.
If the application image has no debugging tools, use an approved temporary debugging container or pod that reproduces the relevant labels and namespace. Keep in mind that a debugging pod with different labels may be selected by different policies.
Check the result carefully:
A connection timeout may indicate filtering, routing problems, or an unreachable destination.
A connection refusal often means the destination was reached but nothing accepted the connection on that port, although network behavior can vary.
A DNS resolution error points to name resolution, which may itself depend on allowed egress traffic.
An HTTP error response usually indicates that a connection reached an HTTP service, even if the application request failed.
These symptoms are clues, not definitive proof of a NetworkPolicy problem.
8. Check DNS and Outbound Traffic
Applications often need to resolve service names before they can connect to other workloads. If egress is restricted, DNS traffic can be blocked along with application traffic.
A common approach is to allow DNS traffic to the cluster's DNS pods. The exact namespace and labels vary, so inspect your cluster before using a policy.
For a cluster where DNS pods run in the kube-system namespace with the label k8s-app: kube-dns, a policy might look like this:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
This permits matching API pods to send DNS traffic to the selected DNS pods over UDP and TCP port 53.
Before applying it, confirm the actual DNS labels and namespace:
kubectl get pods -n kube-system --show-labels
Some clusters use different DNS implementations or labels. DNS traffic may also traverse node-local DNS components, depending on the cluster configuration, so a policy that targets DNS pods might not match the actual traffic path in every environment.
Test name resolution from the affected workload and inspect the DNS configuration if lookups fail.
9. Use a Small Policy to Isolate the Problem
When a policy has several selectors and port rules, temporarily reducing the test case can help identify the incorrect assumption.
For example, if API traffic should be allowed only from frontend pods, verify these items in order:
The API pod has the expected labels.
The frontend pod has the expected labels.
Both pods are in the expected namespaces.
The API application listens on the expected port.
The policy selects the API pod.
The source selector matches the frontend pod.
Other applicable policies have been reviewed.
The network plugin is enforcing policies.
Use read-only commands first:
kubectl get networkpolicy -A
kubectl get pods -n production --show-labels
kubectl get pods -n web --show-labels
kubectl describe networkpolicy allow-frontend-to-api \
-n production
If you change a policy to test a hypothesis, use a controlled environment or a planned change window. Avoid broad allow-all rules in production because they can hide the underlying problem and weaken isolation.
Standard Kubernetes NetworkPolicy does not provide explicit deny rules or a priority mechanism. More advanced capabilities may be available through specific network plugins, but those are implementation-specific and should not be assumed to work in a portable NetworkPolicy manifest.
10. A Practical Troubleshooting Checklist
When a NetworkPolicy appears not to work, use this checklist to narrow down the cause.
Check | What to verify |
|---|
Network plugin | The installed CNI supports and enforces NetworkPolicy |
Pod labels | Selectors match the actual running pods |
Namespace | Source and destination namespaces are correct |
Traffic direction | Ingress, egress, or both are configured as required |
Policy combination | All applicable policies have been reviewed |
Destination port | The rule uses the destination pod's port |
DNS | Required name-resolution traffic is allowed |
Application | The target process is listening and healthy |
Network path | The test reproduces the real application traffic path |
Recent changes | New policies, labels, deployments, or networking changes explain the failure |
Conclusion
When Kubernetes NetworkPolicies do not behave as expected, start by checking enforcement support, selectors, namespaces, and traffic direction. Then review the combined effect of all applicable policies and confirm that the allowed ports match the actual destination pods.
Test from the affected workload rather than relying only on external connectivity checks. If DNS or outbound requests fail, investigate egress rules as well as ingress rules.
A systematic approach makes it easier to distinguish a policy configuration error from an application, DNS, or networking problem-and helps you fix the actual cause without opening more traffic than necessary.
Join the conversation! Your thoughts help the community grow.