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:

Code
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:

YAML
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:

YAML
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:

Code
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:

YAML
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:

YAML
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:

YAML
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:

Code
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.

YAML
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:

YAML
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:

YAML
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:

Bash
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:

Bash
kubectl run network-test \
  --image=curlimages/curl \
  --restart=Never \
  --rm -it -- sh

Then test an allowed service:

Bash
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:

YAML
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:

Bash
kubectl get networkpolicy -A

Then inspect the policy:

Bash
kubectl describe networkpolicy allow-frontend-to-api \
  -n production

Check pod labels:

Bash
kubectl get pods -n production --show-labels

Incorrect labels are a frequent reason for policies not matching the intended workloads.

Also verify Service endpoints:

Bash
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:

Code
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.