Kubernetes makes it easy to deploy applications, but a default cluster does not automatically provide strong network isolation between workloads. If network traffic is left unrestricted, a compromised application may be able to communicate with services that it does not actually need.
Network security policies help reduce this risk by controlling which pods are allowed to communicate with other pods or external endpoints.
For developers, the important part is understanding that NetworkPolicy is not only a security configuration. It can also affect application behavior. A policy that is too restrictive can break service discovery, database connections, monitoring, or external API calls.
This article explains how Kubernetes NetworkPolicy works, how to apply it safely, and how to test policies before using them in production.
Why Kubernetes Workloads Need Network Isolation
Consider a cluster running several applications:
Frontend
|
+----> API
|
+----> Database
Monitoring
|
+----> API
Without network restrictions, other workloads in the cluster may potentially communicate with these services, depending on the cluster networking configuration.
The API may only need access from the frontend and monitoring system. The database may only need to accept connections from the API.
A network policy allows developers to express these communication rules.
For example:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-ingress-policy
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
This policy targets pods labeled app: api and permits incoming TCP traffic on port 8080 from pods labeled app: frontend.
The important point is that NetworkPolicy applies to selected pods. It is not a firewall rule that automatically protects every workload in the cluster.
How NetworkPolicy Works
A NetworkPolicy contains several important sections:
Field | Purpose |
podSelector
| Selects the pods protected by the policy |
policyTypes
| Defines ingress, egress, or both |
ingress
| Defines allowed incoming traffic |
egress
| Defines allowed outgoing traffic |
ports
| Restricts traffic to specific ports |
from
| Defines permitted traffic sources |
to
| Defines permitted traffic destinations |
For example:
spec:
podSelector:
matchLabels:
app: database
policyTypes:
- Ingress
This identifies the database pods as the policy target.
Once ingress isolation is active, traffic must be explicitly allowed according to the applicable policies.
Start With Application Traffic Mapping
Do not create NetworkPolicies by guessing.
First identify the traffic your application actually requires.
For a typical three-tier application:
Internet
|
Ingress
|
Frontend
|
API
|
Database
The required communication might be:
Source | Destination | Port | Required |
Ingress | Frontend | 80 | Yes |
Frontend | API | 8080 | Yes |
API | Database | 5432 | Yes |
API | External API | 443 | Yes |
Monitoring | API | 8080 | Maybe |
Database | Internet | Any | Usually no |
This traffic map becomes the foundation for your policies.
It also prevents a common problem: creating a policy that blocks a dependency that nobody remembered to document.
Apply a Default-Deny Strategy Carefully
A common security approach is to start with default deny and then explicitly allow required communication.
For ingress:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
The empty podSelector selects all pods in the namespace.
This creates an important security boundary, but it can also cause application failures if required traffic has not been allowed.
For that reason, apply default-deny policies in a controlled environment first.
Allow Application-to-Application Traffic
After establishing the default restriction, add specific rules.
For example, allow the frontend to access the API:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-api
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
This is preferable to allowing the entire namespace to access the API.
The rule expresses the application's actual dependency rather than creating a broad network exception.
Control Database Access
Databases are usually good candidates for strict network policies.
Suppose PostgreSQL runs as a Kubernetes workload:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-api-to-postgres
namespace: production
spec:
podSelector:
matchLabels:
app: postgres
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: api
ports:
- protocol: TCP
port: 5432
Now the database policy describes a simple rule:
API pod -> PostgreSQL:5432 -> Allowed
Other pod -> PostgreSQL:5432 -> Not allowed by this policy
This is much safer than opening database access to every workload in the namespace.
Use Namespace Selectors When Needed
Sometimes the source workload exists in another namespace.
In that case, a namespace selector can be used.
from:
- namespaceSelector:
matchLabels:
environment: monitoring
podSelector:
matchLabels:
app: prometheus
This allows traffic from pods matching app: prometheus in namespaces labeled environment: monitoring.
Using both selectors is useful when you need to restrict traffic to a specific application in a specific namespace.
Be careful with namespace labels because changing those labels can change which workloads are allowed to communicate.
Control Outbound Traffic
Ingress controls traffic coming into a pod. Egress controls traffic leaving it.
For example, an API may need access to an external HTTPS service:
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: api-egress
namespace: production
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
ports:
- protocol: TCP
port: 443
This example allows HTTPS traffic to destinations covered by the specified CIDR.
However, broad egress rules should be used carefully. If an application only needs access to a small set of destinations, a more restrictive design is preferable.
Do Not Forget DNS
One of the most common NetworkPolicy mistakes is blocking DNS.
Applications frequently resolve Kubernetes Services through cluster DNS.
If egress traffic is restricted, DNS traffic may also need to be explicitly allowed.
A typical DNS rule can look like this:
egress:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
The exact labels used by DNS pods can vary by Kubernetes distribution and cluster configuration.
Before applying such a rule, inspect the DNS deployment in the actual cluster:
kubectl get pods -n kube-system --show-labels
Do not copy labels blindly from another cluster.
Test Network Policies Before Production
Network policies should be tested using both positive and negative scenarios.
For example, create a temporary test pod:
kubectl run network-test \
--image=curlimages/curl \
--restart=Never \
--rm -it -- sh
Then test an allowed service:
curl http://api.production.svc.cluster.local:8080/health
Also test a service that should be blocked.
The expected behavior should be documented before testing:
Test | Expected Result |
Frontend → API | Allowed |
API → Database | Allowed |
Frontend → Database | Blocked |
Random pod → Database | Blocked |
API → Required external HTTPS service | Allowed |
API → Unnecessary destination | Blocked |
This approach makes policy testing objective rather than relying on assumptions.
Common NetworkPolicy Mistakes
Applying Default Deny Without Dependency Mapping
A default-deny policy can immediately break application communication.
Always document required traffic first.
Forgetting DNS
Applications may suddenly report connection or name-resolution failures after egress restrictions are introduced.
Check DNS early when troubleshooting.
Assuming NetworkPolicy Is Supported Everywhere
NetworkPolicy behavior depends on the cluster's network implementation.
Verify that the installed CNI or networking implementation supports the policy features you intend to use.
Using Broad Selectors
Rules such as:
from:
- namespaceSelector: {}
can allow traffic from every namespace.
Use narrower selectors when the application architecture permits it.
Allowing Too Much Egress
A policy that allows unrestricted outbound traffic provides less security value.
Review external dependencies and restrict outbound access where practical.
Troubleshooting NetworkPolicy Problems
When an application stops communicating after a policy change, inspect the policies first:
kubectl get networkpolicy -A
Then inspect the policy:
kubectl describe networkpolicy allow-frontend-to-api \
-n production
Check pod labels:
kubectl get pods -n production --show-labels
Incorrect labels are a frequent reason for policies not matching the intended workloads.
Also verify Service endpoints:
kubectl get endpoints -n production
If the Service has no valid endpoints, the problem may not be the NetworkPolicy at all.
When debugging, test one network path at a time:
Client pod
|
v
Service
|
v
Target pod
This makes it easier to determine where connectivity is being blocked.
Best Practices
Use application labels that clearly represent workload roles, such as app, component, or tier.
Keep NetworkPolicies in version control alongside application deployment manifests.
Start with a documented traffic matrix before writing policies.
Prefer narrow allow rules over broad namespace-wide access.
Test both allowed and denied traffic.
Include DNS and monitoring dependencies in the traffic design.
Apply policies gradually in non-production environments.
Review policies whenever application architecture changes.
Do not assume that a policy that works in one Kubernetes environment will behave identically in another without checking the underlying networking implementation.
Advantages and Disadvantages
Advantages | Disadvantages |
Reduces unnecessary network access | Requires traffic analysis |
Limits lateral movement between workloads | Incorrect policies can break applications |
Makes application communication explicit | Troubleshooting becomes more involved |
Supports namespace and pod-level isolation | Requires compatible networking support |
Can protect sensitive services such as databases | Policies must be maintained as applications change |
Summary
Kubernetes NetworkPolicy provides a practical way to control communication between workloads and reduce unnecessary network exposure.
The most effective approach is to begin with the application's real communication requirements. Map frontend, API, database, DNS, monitoring, and external service dependencies before creating restrictive policies.
Use default-deny rules carefully, then add narrowly scoped allow rules for required traffic. Test both successful and blocked communication paths, and always verify pod labels and cluster networking support when troubleshooting.
Network security should be treated as part of application architecture rather than something added after deployment. A well-designed policy makes the allowed communication paths clear while reducing the number of unnecessary connections available to workloads in the cluster.
Join the conversation! Your thoughts help the community grow.