Kubernetes
Network Policy

Published: October 28, 2026 · 8 min read

Kubernetes Network Policy Explained Simply

  • By default the cluster network is flat — any Pod reaches any Pod.
  • A NetworkPolicy is a firewall rule for Pods.
  • It is namespaced — one per namespace.
  • Ingress is traffic coming in. Egress is traffic going out.
  • The moment a policy selects a Pod, that direction becomes deny by default.
  • Policies only add allowed traffic. There is no deny rule.
  • Blocked traffic times out, it is never refused.
  • It needs a CNI plugin that supports it, or nothing happens.

By default every Kubernetes pod can reach every other pod until a network policy is added

The Problem

A namespace groups your objects. It does not wall them off.

Your frontend, backend, database and metrics Pods can all reach each other. So can Pods in other namespaces.

If one Pod is compromised, your database is one curl away.

🚫

A namespace is a label on a shelf, not a locked door. The door is a NetworkPolicy.

Ingress and Egress

Ingress is traffic into the pod and egress is traffic out of the pod in a Kubernetes network policy

Always read the direction from the Pod the policy selects.

DirectionMeansYou write
Ingresswho may come infrom:
Egresswhere it may go outto:

Most policies only use Ingress. That alone fixes the biggest problem.

Your First Policy

Only the frontend may reach the backend, and only on port 8080.

netpol.yaml
# netpol.yaml  -  only the frontend may reach the backend

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-allow-frontend
  namespace: hotel
spec:
  podSelector:                    # WHICH Pods this policy protects
    matchLabels:
      app: backend
  policyTypes:
    - Ingress                     # we are controlling traffic coming IN
  ingress:
    - from:
        - podSelector:            # WHO is allowed in
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080              # and on which port

Four blocks, four questions:

BlockQuestion it answers
podSelectorwhich Pods does this policy protect?
policyTypeswhich direction am I controlling?
ingress.fromwho is allowed?
ingress.portson which port?
⚠️

podSelector appears twice and means two different things. At the top it picks the Pods being protected. Inside from: it picks the Pods being allowed.

terminal — apply
kubectl apply -f netpol.yaml
# networkpolicy.networking.k8s.io/backend-allow-frontend created

kubectl get networkpolicy -n hotel
# NAME                     POD-SELECTOR   AGE
# backend-allow-frontend   app=backend    10s

kubectl describe networkpolicy backend-allow-frontend -n hotel
# Spec:
#   PodSelector: app=backend
#   Allowing ingress traffic:
#     To Port: 8080/TCP
#     From:
#       PodSelector: app=frontend
#   Not affecting egress traffic
#   Policy Types: Ingress

Deny by Default

Once a Kubernetes network policy selects a pod that direction becomes deny by default

This is the rule everything else depends on.

StateWhat happens
No policy selects the Podeverything is allowed
One policy selects it for Ingressnothing comes in, except what is listed

One small policy flips a Pod from fully open to fully closed. Then you open it back up, one rule at a time.

Policies are added together. Two policies on the same Pod do not conflict and have no order. If either one allows a connection, it is allowed.

Testing It

terminal — test
# from the frontend Pod - allowed
kubectl exec -it frontend-6b9f4d7c21-k8ptm -n hotel -- \
  curl -s -m 3 http://backend-svc:8080/health
# ok

# from any other Pod - blocked
kubectl exec -it metrics-7d4b9c6f85-2kq9x -n hotel -- \
  curl -s -m 3 http://backend-svc:8080/health
# curl: (28) Connection timed out after 3001 milliseconds

# blocked traffic is DROPPED, not refused
# you get a timeout, never "connection refused"

Look at the failure. It is a timeout, not connection refused.

🚫

Blocked traffic is dropped silently. A timeout looks exactly like a crashed Pod or a wrong address. When something that used to work starts hanging, check for a NetworkPolicy first.

The Three Selectors

The three selectors in a Kubernetes network policy, podSelector namespaceSelector and ipBlock

netpol.yaml — selectors
# the three things you can put under from:

ingress:
  - from:
      # 1. Pods in the SAME namespace
      - podSelector:
          matchLabels:
            app: frontend

      # 2. every Pod in ANOTHER namespace
      - namespaceSelector:
          matchLabels:
            name: monitoring

      # 3. plain IP ranges, outside the cluster
      - ipBlock:
          cidr: 10.0.0.0/16
          except:
            - 10.0.5.0/24
SelectorAllows
podSelectorPods with a label, in the same namespace
namespaceSelectorevery Pod in another namespace
ipBlockplain IP ranges, outside the cluster

One Dash Changes Everything

This trips up almost everyone, so look at it closely.

OR vs AND
# A LIST of entries  =  OR
ingress:
  - from:
      - podSelector:
          matchLabels:
            app: frontend
      - namespaceSelector:              # a NEW list item
          matchLabels:
            name: monitoring
# allowed: frontend Pods  OR  anything in monitoring


# ONE entry with two selectors  =  AND
ingress:
  - from:
      - namespaceSelector:
          matchLabels:
            name: monitoring
        podSelector:                    # same item, no dash
          matchLabels:
            app: prometheus
# allowed: ONLY prometheus Pods inside monitoring

# the dash is the whole difference
Written asMeans
Two list items, each with a dashOR — either one is enough
One list item holding two selectorsAND — both must match
⚠️

Get this wrong and you open far more than you meant to. A missing dash turns a narrow rule into a wide one.

Lock a Whole Namespace

Start by closing everything, then open what you need.

deny-all.yaml
# deny-all.yaml  -  nothing may enter any Pod in this namespace

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: hotel
spec:
  podSelector: {}                 # {} means EVERY Pod in the namespace
  policyTypes:
    - Ingress
  # no ingress: block at all, so nothing is allowed in

Two details make this work:

  • podSelector: {} — an empty selector matches every Pod in the namespace.
  • No ingress: block at all — so nothing is allowed in.

Apply this first, then add one policy per connection you actually want.

Controlling Egress

netpol.yaml — egress
# allow the backend to reach the database and DNS only

spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: database
      ports:
        - protocol: TCP
          port: 5432

    - to:                         # DNS, or every name lookup fails
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
🚫

Add an egress policy and you must allow DNS on port 53 to kube-system. Forget it and every hostname lookup fails. This is the single most common egress mistake.

It Needs the Right CNI

Kubernetes accepts the NetworkPolicy object. It does not enforce it.

Your network plugin does that work, and not all of them can.

PluginEnforces policies?
Calicoyes
Ciliumyes
Weave Netyes
Flannel (on its own)no
kubectl get pods -n kube-system | grep -iE 'calico|cilium|weave'
⚠️

With a plugin that does not support them, your policy applies with no error and does absolutely nothing. Check this before you spend an hour debugging a rule.

Network Policy Commands

commands
# look
kubectl get networkpolicy -n <namespace>
kubectl get netpol -A                        # short form, every namespace
kubectl describe netpol <name> -n <namespace>

# which Pods does a policy actually select?
kubectl get pods -n <namespace> -l app=backend

# test from inside a Pod
kubectl exec -it <pod> -n <namespace> -- curl -m 3 http://<service>:<port>

# is your CNI even enforcing policies?
kubectl get pods -n kube-system | grep -iE 'calico|cilium|weave'

# delete
kubectl delete netpol <name> -n <namespace>
CommandWhat it does
kubectl get netpol -n <ns>list policies in a namespace
kubectl get netpol -Alist them across every namespace
kubectl describe netpol <name> -n <ns>the rules, in plain words
kubectl get pods -n <ns> -l app=backendwhich Pods a selector matches
kubectl exec -it <pod> -- curl -m 3 <url>test a connection from inside
kubectl delete netpol <name> -n <ns>remove it

What's Next

A NetworkPolicy turns a flat, open cluster into one where every connection is on purpose. Start with a default deny on one namespace, then open the connections you actually need. The official NetworkPolicy page (opens in a new tab) and the recipes collection (opens in a new tab) have ready made examples.

← Back to ConfigMap and Secret

Continue to EKS Storage Setup →


Frequently Asked Questions

What is a Network Policy in Kubernetes?

A NetworkPolicy is a firewall rule for Pods. By default every Pod in a cluster can reach every other Pod, with nothing in the way. A NetworkPolicy lets you say which Pods may talk to which, on which ports. It is a namespaced object, so you write one for each namespace you want to protect.

Can all pods talk to each other in Kubernetes by default?

Yes. A fresh cluster has a flat network. Any Pod can reach any other Pod in any namespace, and nothing stops it. There is no firewall between Pods until you create a NetworkPolicy. That is why one compromised Pod can reach your database straight away.

What is the difference between ingress and egress in a Network Policy?

Read both from the Pod the policy selects. Ingress is traffic coming in to that Pod, and you list who is allowed under from. Egress is traffic leaving that Pod, and you list where it may go under to. Most policies only use ingress. Add egress when you also need to control what the Pod is allowed to call.

What happens when a Network Policy selects a Pod?

That direction becomes deny by default for it. Before any policy selects a Pod, everything is allowed. The moment one policy selects it and lists Ingress in policyTypes, everything incoming is blocked. Only what the policy allows gets through. Nothing changes for egress unless you list Egress too.

What is podSelector in a Kubernetes Network Policy?

podSelector appears in two places and means something different in each. At the top of the spec it chooses which Pods the policy protects. Inside a from or to block it chooses which Pods are allowed. An empty podSelector written as a pair of braces means every Pod in the namespace.

How do you create a default deny Network Policy?

Write a policy whose podSelector is an empty pair of braces. List Ingress under policyTypes, and give it no ingress rules at all. The empty selector matches every Pod in the namespace, and with no rules nothing is allowed in. You then add a separate policy for each connection you want to permit.

Why is my Kubernetes Network Policy not working?

Almost always the CNI plugin. Kubernetes accepts the NetworkPolicy object but does not enforce it itself. Your network plugin has to implement it. Calico, Cilium and Weave do. Plain Flannel does not, so the policy applies cleanly and changes nothing. Check which plugin is running in kube-system before you debug anything else.

Does a Network Policy block traffic or refuse it?

It drops the packets silently. The caller gets a timeout, never a connection refused error. This matters when you are debugging, because a timeout looks exactly like a crashed Pod or a wrong address. If a connection that used to work now hangs, check for a NetworkPolicy first.