NetworkPolicy Builder

Generate Kubernetes NetworkPolicies with plain-English explanations. Catches the gotchas — additive policies, AND vs OR peer selectors, broken DNS — before they ship. Runs entirely in your browser.

Target

what does this policy apply to?
Pod selector

No labels — same as 'all pods'. Add at least one label to scope.

For matchExpressions support, edit the YAML directly after generating.

Direction

at least one is required

Ingress and Egress are independent. Selecting only one does not restrict the other.

Ingress rules

default-deny

No rules — this is a default-deny ingress policy. Add a rule to allow specific inbound traffic, or leave empty to deny all.

1 warning

Policy name is the default. Policy name is the default. Pick a descriptive name (`allow-api-from-web`, `default-deny-egress`) so it's findable in `kubectl get netpol`.

network-policy.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: network-policy
  namespace: "<namespace>"
spec:
  podSelector: {}
  policyTypes:
    - Ingress
Apply with kubectlheredoc — no file write
kubectl apply -f - <<'EOF'
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: network-policy
  namespace: "<namespace>"
spec:
  podSelector: {}
  policyTypes:
    - Ingress
EOF

Then verify it landed:

kubectl describe networkpolicy/network-policy

How NetworkPolicy actually works

NetworkPolicy is the Kubernetes resource where the most experienced operators are still wrong half the time. The model is small (four real fields plus selectors), but the defaults are non-obvious in ways that have shipped to production at every shop that runs a cluster long enough.

A NetworkPolicy selects a set of pods and declares what traffic they're allowed to send or receive. There is no deny rule — only allow rules and the absence of them. If no policy selects a pod, all traffic is permitted. If any policy selects a pod, only the union of those policies' allow rules permits traffic to or from it; everything else is denied.

Key insight

NetworkPolicy is allow-list, additive, and direction-aware. The first policy that selects a pod flips it from "everything allowed" to "only the union of allows is allowed" — and Ingress and Egress flip independently. This is the source of every "why is my policy doing nothing" and "why is my policy blocking something I didn't expect" incident.

Six things people get wrong

Default is permissive, not deny

Until at least one NetworkPolicy selects a pod, all traffic to and from that pod is allowed. Fresh clusters and fresh namespaces have no policies, so they're wide open. The first NetworkPolicy you add doesn't tighten things by itself — it tightens them only for the pods it selects. Pods in the same namespace that aren't selected stay wide open.

Policies are additive — there is no deny

Multiple NetworkPolicies on the same pod are OR'd. If policy A allows traffic from app=web and policy B allows traffic from app=metrics, the pod allows both. To deny, you write a policy that selects the pods and lists no rules — the implicit deny applies because nothing else allows. To revoke an allow, you have to delete or modify the policy that grants it; you can't write a "deny" policy that overrides another policy's allow.

AND vs OR in peer selectors

A single peer with both namespaceSelector and podSelector is an intersection: "pods matching X in namespaces matching Y." Two separate peers in the same rule's from/to array are a union: "pods matching X or any pod in namespaces matching Y." The most-broken policies in the wild get this wrong — usually meaning OR but writing AND because the YAML reads more naturally that way.

Omitted selectors have counterintuitive defaults

podSelector: means "all pods in this namespace" — an empty object is a wildcard, not a missing field. Inside a peer, omitting namespaceSelector means "same namespace as the policy" (not "all namespaces"); omitting podSelector means "all pods" within whatever namespace selector is set. The semantics are well-defined in the spec but counterintuitive enough that operators routinely write a policy meaning "same-namespace pods" and get "all pods anywhere."

Ingress and Egress are independent

A policy with policyTypes: [Ingress] does not constrain egress. People write "ingress lockdown" policies and assume outbound is also restricted; it isn't. To control egress, you have to list Egress in policyTypes and write the rules. Likewise an "egress lockdown" policy with no Ingress in policyTypes doesn't restrict inbound traffic.

Default-deny egress breaks DNS

The most common production-incident class: deploying a default-deny-egress policy and watching every pod immediately fail service-to-service lookups. CoreDNS lives in the kube-system namespace; if you don't explicitly allow UDP/53 to it, every nslookup, every gRPC client connecting by name, every kubectl logs from a debug container fails with "name resolution error." Pair every default-deny-egress with an allow-dns-egress companion policy.

Test before applying. NetworkPolicy enforcement requires a CNI that supports it — Calico, Cilium, Weave Net, or recent Flannel with the netpol controller. Vanilla Flannel and a few other CNIs silently ignore policies. Run kubectl get pods -n kube-system and confirm an enforcing CNI is running before you trust any test result.

Templates that make sense

The nine templates in the loader cover the patterns SREs reach for most often:

Debugging "why is my policy doing nothing?"

Almost always one of three things, in this order:

  1. The CNI doesn't enforce policies. Check kubectl get pods -n kube-system for Calico/Cilium/Weave. If it's vanilla Flannel without the netpol controller, the policies are valid YAML the cluster ignores.
  2. The pod doesn't match the policy's selector. Run kubectl get pod <pod> -o jsonpath='{.metadata.labels}' and confirm the labels match what the policy's podSelector targets.
  3. Another policy is allowing the traffic. Run kubectl get networkpolicy -n <ns> -o yaml | less and search for any policy whose rules permit what you're trying to block. Remember: any policy that allows is enough.

Frequently asked questions

My NetworkPolicy seems to do nothing — why?

Most likely your CNI doesn't enforce policies. Vanilla Flannel, basic kindnet, and a few other CNIs accept the YAML but silently ignore it. Run `kubectl get pods -n kube-system` and confirm you see Calico, Cilium, Weave, or another enforcing CNI before you debug the policy itself. Without enforcement, every test will report 'allowed' regardless of what you write.

I added a deny policy but traffic is still flowing — what gives?

There is no 'deny' rule in NetworkPolicy. Policies are *additive*: they only allow. To deny, write a policy that selects the pods and lists no rules — the implicit deny applies because nothing else allows. If another policy on the same pods allows the traffic, the union of their allowances wins.

What's the difference between two peers and one peer with two selectors?

Two separate peers in the same rule are OR — traffic matches if either one matches. A single peer with both `podSelector` and `namespaceSelector` is AND — traffic must match both at once. The most common bug in production NetworkPolicies: meaning OR but writing AND.

My pods can't resolve DNS after I added a default-deny egress policy.

Default-deny egress denies CoreDNS too. Every pod doing service-to-service lookups now fails with "name resolution error" because UDP/53 to kube-system is blocked. Pair every default-deny-egress with an allow-DNS-egress policy (this tool has the template ready).

My ingress lockdown policy doesn't restrict outbound — should it?

Not unless you set policyTypes: [Egress] too. Ingress and Egress are independent fields. A policy with policyTypes: [Ingress] and no egress rules does nothing to outbound traffic — even if you assumed it was a 'lock everything down' policy.

Why use `kubernetes.io/metadata.name` to target a namespace?

On Kubernetes 1.22+, every namespace automatically gets a `kubernetes.io/metadata.name` label equal to its name. That makes namespace selection by name reliable without depending on operators adding their own labels. On older clusters you have to apply a custom label yourself, e.g. `kubectl label ns prod team=backend`.

Should I write one big policy or many small ones?

Many small ones, scoped by purpose. Because policies are additive, separating 'allow ingress from web' from 'allow egress to DNS' from 'allow egress to API' makes each policy reviewable in isolation and easy to remove if you need to. One giant policy is harder to audit and harder to remove safely.