Kubernetes NetworkPolicy is one of those controls that looks simple in YAML and turns complex the moment real applications, DNS, ingress paths, and cross-namespace traffic get involved. This guide is a practical reference for common isolation scenarios: default deny, namespace scoping, app-to-app allowlists, controlled egress, and safe exceptions for platform services. The goal is not to present a perfect universal policy set, but to give you reusable patterns you can adapt as your cluster, CNI behavior, and service boundaries change.
Overview
Use this section to understand what NetworkPolicy can and cannot do before you start copying manifests.
A Kubernetes NetworkPolicy defines which traffic is allowed to and from selected pods. In practice, it is most useful for reducing unnecessary east-west traffic inside the cluster and for limiting accidental exposure between workloads that happen to share the same network. Teams often use it to enforce pod isolation, protect sensitive namespaces, and narrow egress from application workloads.
The important mental model is that policies select pods, not Services. A policy applies to pods matching its selector in a namespace. Once a pod is selected by a policy for ingress or egress, that traffic direction becomes restricted according to the rules you define. That is why a single default-deny policy can have a large effect, even though it looks small.
There is also a practical caveat: NetworkPolicy support depends on your CNI plugin and how it implements policy semantics. The Kubernetes API gives you a common language, but behavior around advanced cases can vary. If you are writing a cluster hardening standard, treat every pattern in this article as something to test in your own environment rather than assume.
Another source of confusion is the difference between isolation and exposure. You can expose an application through an Ingress or a LoadBalancer Service and still restrict pod-to-pod communication internally. Likewise, a workload that is not publicly exposed may still be overly reachable from other namespaces. NetworkPolicy is about controlling network paths, not replacing identity, secrets, admission control, or image security. For adjacent hardening work, it pairs well with a review of Dockerfile best practices for smaller, safer, faster images and a broader look at secrets management for cloud native teams.
If you remember only three things, make them these: start with a default deny posture, add explicit allows for known flows, and test DNS plus platform dependencies early. Most failed rollouts happen because one of those three steps was skipped.
Core framework
Use this framework to design policies in a way that stays understandable as your cluster grows.
A reliable approach is to think in layers rather than individual YAML files:
- Namespace intent: Decide whether a namespace is open by default, isolated by default, or only partially isolated.
- Application identity: Label workloads consistently so selectors are predictable. For example, use labels like
app,component,tier, andpart-of. - Required ingress paths: Map which callers should reach which pods and on which ports.
- Required egress paths: Identify DNS, external APIs, databases, object storage, or internal services the app needs.
- Platform exceptions: Account for ingress controllers, monitoring agents, service mesh components, or node-local services if they are part of your setup.
This leads to a clean sequence:
- Apply a namespace-level default deny.
- Add DNS egress if needed.
- Add app-specific ingress allows.
- Add narrowly scoped egress rules.
- Verify with real traffic, not just YAML review.
Label strategy matters more than many teams expect. A policy is only as good as the selectors it depends on. If one team labels a frontend pod with app=web and another uses name=frontend, reusable patterns become fragile. Standard labels help turn policy writing from a one-off task into a repeatable platform workflow.
It also helps to separate policy goals:
- Baseline policies that every namespace gets, such as default deny and DNS.
- Application policies that define expected service-to-service traffic.
- Environment policies for special cases like staging test tools, batch jobs, or migration workloads.
Finally, decide how you will validate changes. In many teams, NetworkPolicy issues show up first as application timeouts, failed health checks, or confusing startup errors. Pair policy changes with observability and troubleshooting habits. Articles like structured logging best practices for Kubernetes and microservices and this Kubernetes pod status guide are useful companions because policy mistakes often look like generic connectivity or readiness problems.
Practical examples
Use these patterns as starting points, then adapt selectors, ports, and namespaces to your environment.
1) Default deny all ingress in a namespace
This is the most common starting point for Kubernetes pod isolation. It prevents incoming traffic to selected pods unless another policy allows it.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-ingress
namespace: app-space
spec:
podSelector: {}
policyTypes:
- IngressWhy it is useful: it creates a clear baseline. Any allowed path now has to be intentional.
When to use it: almost any namespace with application workloads, especially shared clusters.
2) Default deny both ingress and egress
If you want stricter isolation, restrict both directions. This is a common network policy deny all example for security-sensitive namespaces.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: payments
spec:
podSelector: {}
policyTypes:
- Ingress
- EgressWhy it is useful: it blocks unexpected outbound traffic, not just inbound calls.
Watch for: DNS resolution and external dependencies will stop working until explicitly allowed.
3) Allow DNS egress after a default deny
Many workloads break immediately after egress lockdown because they still need DNS. The exact labels for your DNS pods may vary by cluster, so treat selectors here as placeholders.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-dns
namespace: payments
spec:
podSelector: {}
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: 53Why it is useful: it restores a core dependency without opening broad outbound access.
Watch for: some environments use different labels or DNS deployment names.
4) Allow ingress from an ingress controller to frontend pods
This pattern is common when public traffic should enter through an ingress controller and only reach designated frontend pods.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-ingress-controller
namespace: shop
spec:
podSelector:
matchLabels:
app: frontend
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-system
podSelector:
matchLabels:
app: ingress-controller
ports:
- protocol: TCP
port: 8080Why it is useful: it keeps frontend pods from being reachable by every other workload in the cluster.
Watch for: if the controller namespace or labels change during upgrades, this policy may silently stop matching.
5) Allow frontend to call backend, and only on the app port
This is a straightforward service-to-service allowlist.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: backend-allow-from-frontend
namespace: shop
spec:
podSelector:
matchLabels:
app: backend
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 9000Why it is useful: it expresses an application boundary in one place and limits lateral movement.
Watch for: this only governs traffic to backend pods. If frontend also needs controlled egress, add a separate egress policy.
6) Allow traffic from one namespace but deny others by default
For shared clusters, namespace-level isolation is often more important than individual pod labels.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-monitoring-scrape
namespace: shop
spec:
podSelector:
matchLabels:
metrics: enabled
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: monitoring
ports:
- protocol: TCP
port: 9090Why it is useful: it allows a platform namespace to do its job without broadly exposing metrics endpoints.
Watch for: scrape failures may look like monitoring issues rather than policy issues. If you are reviewing metric tooling at the same time, see Prometheus vs Grafana Cloud vs Datadog for metrics monitoring.
7) Restrict egress to a database in another namespace
This pattern is useful when only one application should reach a stateful backend.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-egress-to-db
namespace: app-space
spec:
podSelector:
matchLabels:
app: api
policyTypes:
- Egress
egress:
- to:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: data
podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432Why it is useful: it narrows outbound paths to only the intended dependency.
Watch for: backup jobs, migration jobs, and admin tools may need separate rules.
8) Isolate one sensitive workload from peers in the same namespace
Sometimes the problem is not cross-namespace access but unrelated pods living side by side. You can isolate only the sensitive target.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: isolate-admin-api
namespace: internal-tools
spec:
podSelector:
matchLabels:
app: admin-api
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: bastion
ports:
- protocol: TCP
port: 8443Why it is useful: it protects high-risk endpoints without forcing a full namespace redesign.
9) Permit egress to an external CIDR range
Some CNIs support typical CIDR-based egress controls via ipBlock. This can help when a workload must call a known external service range.
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-external-api-egress
namespace: integrations
spec:
podSelector:
matchLabels:
app: sync-worker
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443Why it is useful: it is narrower than unrestricted internet egress.
Watch for: external provider ranges change. This is one of the least evergreen policy styles unless backed by stable network controls outside the cluster.
Across all of these examples, the recurring lesson is that readable labels and small, intention-revealing policies age better than large all-in-one manifests.
Common mistakes
Use this section as a pre-deployment checklist for avoiding the most common policy failures.
Assuming policy enforcement exists. A NetworkPolicy resource can be accepted by the API even if your effective enforcement is not what you expect. Always verify behavior with your CNI in a test namespace.
Forgetting that policies are additive allows. Multiple policies can apply to the same pod, and the resulting allowed traffic is the union of what those policies permit. Teams sometimes expect one policy to override another. That is not how the model works.
Breaking DNS or platform traffic. Egress-deny policies often fail because no one accounted for DNS, admission webhooks, metrics scraping, sidecars, or node-local services.
Using unstable labels. If policies depend on labels that are manually applied, inconsistently named, or changed during deployments, matching becomes unreliable. Standardize labels early.
Selecting too many pods with podSelector: {}. Empty selectors are useful for namespace-wide defaults, but dangerous when used casually. Confirm the namespace and intent before applying.
Not documenting expected flows. A policy file without a short explanation tends to become tribal knowledge. Add annotations or repository documentation that explains why the rule exists and what traffic it protects.
Troubleshooting from the wrong layer. Policy problems often present as app errors, failed rollouts, or elevated latency. If you are changing deployment strategies, articles like blue-green vs canary deployment can help frame rollout risk, but also remember to ask whether a new pod simply cannot talk to what it needs.
Trying to solve everything with NetworkPolicy. It is a network isolation tool, not a complete security model. You still need workload identity, secrets hygiene, sensible API protections, and reliability instrumentation. For example, if you expose internal APIs, policy design works best alongside thinking about API rate limiting strategies and service objectives such as those discussed in this reliability guide.
When to revisit
Revisit your NetworkPolicy set whenever the cluster’s traffic shape changes, not only after a security incident.
A useful review cadence is to check policies when any of the following happens:
- A new CNI, service mesh, or ingress architecture is introduced.
- Namespaces are reorganized or platform teams adopt new labeling conventions.
- An application gains a new outbound dependency such as an external API, queue, or managed database.
- Monitoring, logging, or security agents are added to the cluster.
- A deployment model changes and new pods need temporary or phased access.
- Audit or compliance work requires stronger separation between workloads.
For practical maintenance, keep an inventory of expected traffic flows per application: inbound callers, outbound dependencies, namespaces involved, and required ports. When something changes, update that inventory first, then update policy manifests. This is much easier than trying to reconstruct intent from production failures later.
A simple action plan looks like this:
- Pick one noncritical namespace.
- Apply a default deny ingress policy.
- Add explicit allows for ingress controller, monitoring, and app-to-app traffic.
- If needed, move to default deny egress and restore DNS first.
- Test connectivity with short-lived debug pods and application smoke tests.
- Promote the pattern into a reusable baseline for other namespaces.
If you treat Kubernetes network policy examples as living patterns rather than one-time snippets, they become much more valuable. The YAML itself is not the hard part. The durable work is keeping traffic intent explicit as your workloads, teams, and cluster components evolve.