🎉 DevOps Interview Prep Bundle is live — 1000+ Q&A across 20 topicsGet it →
All Articles

What is a Kubernetes Network Policy — Explained Simply

By default, all pods in Kubernetes can talk to each other. Network Policies let you control exactly which pods can communicate. Here's how they work with practical examples.

DevOpsBoysJun 4, 20263 min read
Share:Tweet

By default, every pod in your Kubernetes cluster can talk to every other pod. Your database can be reached by your frontend, your CI runner, and potentially anything else in the cluster.

Network Policies let you lock this down.


The Default: Everything Can Talk to Everything

frontend-pod  ──→  backend-pod   ✅ (makes sense)
frontend-pod  ──→  database-pod  ✅ (probably shouldn't!)
ci-runner-pod ──→  database-pod  ✅ (definitely shouldn't!)

Without Network Policies, your cluster has zero network segmentation.


What a Network Policy Does

A Network Policy is a Kubernetes resource that defines:

  • Which pods can send traffic TO a pod (ingress rules)
  • Which pods a pod can send traffic TO (egress rules)
yaml
# Allow only backend pods to talk to the database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: database-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres      # This policy applies TO postgres pods
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend  # Only pods labeled app=backend can connect
      ports:
        - protocol: TCP
          port: 5432

After applying this: only app=backend pods can reach postgres on port 5432. Everything else is blocked.


Important: Network Policies Require a CNI That Supports Them

Not all CNIs enforce Network Policies. The default kubenet doesn't. You need:

  • Calico ✅
  • Cilium ✅
  • Weave ✅
  • Flannel ❌ (does not enforce)

Check your CNI before relying on Network Policies for security.


The Default Deny Pattern

Best practice: start by denying all traffic, then explicitly allow what's needed.

yaml
# Step 1: Deny all ingress to all pods in namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}    # Empty = applies to ALL pods
  policyTypes:
    - Ingress
---
# Step 2: Allow frontend → backend
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-backend
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - port: 8080
---
# Step 3: Allow backend → database
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-db
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: postgres
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend
      ports:
        - port: 5432

Cross-Namespace Communication

yaml
# Allow monitoring namespace to scrape metrics from production namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-prometheus-scrape
  namespace: production
spec:
  podSelector: {}
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: monitoring
      ports:
        - port: 9090

Allow External Traffic (Ingress Controller)

yaml
# Allow nginx ingress controller to reach your app
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-controller
spec:
  podSelector:
    matchLabels:
      app: my-app
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx

Egress Policies

Control what pods can call:

yaml
# Backend can only talk to database and DNS, nothing else
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: backend-egress
spec:
  podSelector:
    matchLabels:
      app: backend
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - port: 5432
    - to:           # Always allow DNS
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP

Testing Your Network Policies

bash
# Test connectivity between pods
kubectl run test-pod --image=busybox --rm -it -- wget -qO- http://backend-service:8080/health
 
# From a pod that should be DENIED:
kubectl run deny-test --image=busybox --rm -it \
  -l app=other \
  -- wget --timeout=3 -qO- http://postgres:5432
# Should timeout — policy is working

Common Mistakes

Forgetting DNS egress — If you add egress policies, always allow UDP/TCP port 53 to kube-dns. Without it, your pods can't resolve service names and everything breaks.

Thinking Network Policies encrypt traffic — They don't. They control access, not encryption. For encryption, use a service mesh (Istio, Linkerd, Cilium).

Applying to a namespace that doesn't have Network Policy support — Policies silently do nothing on CNIs that don't enforce them.

Practice Kubernetes security including Network Policies at KodeKloud.

🔧

Today I Fixed

Short real fixes from production — posted daily

Browse fixes
Newsletter

Stay ahead of the curve

Get the latest DevOps, Kubernetes, AWS, and AI/ML guides delivered straight to your inbox. No spam — just practical engineering content.

Related Articles

Comments