Skip to main content

Kubernetes Network Policies for Pod-Level Traffic Control

By default, every pod in a Kubernetes cluster can talk to every other pod — regardless of namespace, team, or sensitivity. Network Policies are Kubernetes firewall rules that restrict this. They define which pods are allowed to send traffic to which other pods, and which external IPs can reach your services. Without them, a compromised frontend pod can directly connect to your production database.

52 Terms

By default, every pod in a Kubernetes cluster can talk to every other pod — regardless of namespace, team, or sensitivity. Network Policies are Kubernetes firewall rules that restrict this. They define which pods are allowed to send traffic to which other pods, and which external IPs can reach your services. Without them, a compromised frontend pod can directly connect to your production database.

+++

Kubernetes Network Policies for Pod-Level Traffic Control

The Default Problem — Every Pod Talks to Every Pod

When you create a cluster without any NetworkPolicy resources, Kubernetes operates in full-open mode. Every pod, in every namespace, can reach every other pod by IP.

◈ DIAGRAM
Default cluster — no NetworkPolicy:
[frontend pod] ──────────────────→ [payments-db pod] ← Direct DB access
[user-service] ──────────────────→ [payments-db pod] ← Should not be allowed
[logging-agent] ──────────────────→ [payments-db pod] ← Definitely not allowed
A compromised frontend can attempt a SQL injection or data dump directly.

Network Policies change this to an explicit allowlist model.

How Network Policies Work

A NetworkPolicy is applied to a set of pods (the target) and defines two things:

  • Ingress rules: Which pods/IPs are allowed to send traffic to the target
  • Egress rules: Which pods/IPs the target is allowed to send traffic to

As soon as a NetworkPolicy selects a pod, that pod moves from "allow all" to "deny all except what the policy explicitly permits."

◈ DIAGRAM
Before NetworkPolicy:
[any pod] → [payments-db] ✓ allowed
After applying a NetworkPolicy that selects payments-db:
[any pod] → [payments-db] ✗ denied by default
[payments-api pod] → [payments-db] ✓ allowed (explicitly permitted in the policy)
Requirement

: NetworkPolicy enforcement requires a CNI plugin that supports it — Calico, Cilium, or Weave. The default CNI on many clusters (Flannel) does not enforce NetworkPolicy. The policies exist in the API but are silently ignored.

The Most Important Policy — Default Deny All

Start with a deny-all policy for your namespace, then add explicit allow rules. This is the production-safe baseline.

YAML
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-all
namespace: production
spec:
podSelector: {} # Empty selector = applies to ALL pods in this namespace
policyTypes:
- Ingress
- Egress
# No ingress or egress rules = deny everything
Bash
kubectl apply -f default-deny-all.yaml
# After this, ALL pods in 'production' namespace:
# - Cannot receive any traffic (ingress blocked)
# - Cannot send any traffic (egress blocked)
# Now add explicit allow rules for each service

Allowing Specific Pod-to-Pod Traffic

YAML
# allow-payments-api-to-db.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-payments-api-to-db
namespace: production
spec:
podSelector:
matchLabels:
app: payments-db # This policy protects the DB pods
policyTypes:
- Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: payments-api # Only payments-api pods can connect
ports:
- protocol: TCP
port: 5432 # Only on port 5432 (PostgreSQL)
◈ DIAGRAM
Result:
[payments-api pod] → port 5432 → [payments-db pod] ✓ ALLOWED
[frontend pod] → port 5432 → [payments-db pod] ✗ DENIED
[user-service pod] → port 5432 → [payments-db pod] ✗ DENIED
[any other pod] → any port → [payments-db pod] ✗ DENIED

Cross-Namespace Traffic

At Razorpay or Zerodha, different teams deploy into different namespaces. An API gateway in the gateway namespace needs to reach services in the production namespace.

YAML
# allow-gateway-namespace.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-from-gateway-ns
namespace: production
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
name: gateway # Allow from any pod in the 'gateway' namespace
podSelector:
matchLabels:
app: nginx-ingress # But only from nginx-ingress pods specifically
ports:
- protocol: TCP
port: 8080
Bash
# Namespaces need a label for namespaceSelector to work
# Add a label to the gateway namespace first
kubectl label namespace gateway name=gateway
# Verify the label
kubectl get namespace gateway --show-labels

Egress Rules — Controlling Outbound Traffic

Egress rules control what a pod can connect to. This is critical for preventing data exfiltration — a compromised pod calling out to an attacker's server.

YAML
# payments-api-egress.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: payments-api-egress
namespace: production
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Egress
egress:
- to:
- podSelector:
matchLabels:
app: payments-db # Allow outbound to DB
ports:
- protocol: TCP
port: 5432
- to:
- podSelector:
matchLabels:
app: redis-cache # Allow outbound to Redis
ports:
- protocol: TCP
port: 6379
- ports: # Allow DNS resolution (critical — without this,
- protocol: UDP # the pod cannot resolve any hostname)
port: 53
- protocol: TCP
port: 53
Always allow DNS (port 53)

in egress policies. If you block UDP port 53, the pod cannot resolve any DNS names — including kubernetes.default.svc.cluster.local. This silently breaks all service discovery and HTTP calls even to pods you've explicitly allowed.

Allowing External Traffic (from Outside the Cluster)

Traffic from outside the cluster arrives through a LoadBalancer or Ingress controller. The Ingress controller's pod needs to be allowed to reach your application pods.

YAML
# allow-ingress-controller.yaml
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-ingress-controller
namespace: production
spec:
podSelector:
matchLabels:
app: payments-api
policyTypes:
- Ingress
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: ingress-nginx # The ingress controller namespace
ports:
- protocol: TCP
port: 8080

Allowing Traffic from Specific IP Ranges

For integrating with external payment gateways or banking APIs that have fixed IP ranges:

YAML
ingress:
- from:
- ipBlock:
cidr: 103.21.244.0/22 # Cloudflare IP range example
except:
- 103.21.244.100/32 # Exclude a specific IP within the range
ports:
- protocol: TCP
port: 443

Testing That Your Policy Works

Never assume a NetworkPolicy is working. Test it explicitly.

Bash
# Deploy a test pod in the same namespace
kubectl run test-pod \
--image=busybox \
--rm -it \
--restart=Never \
-n production \
-- sh
# Inside the test pod — try connecting to payments-db
# Replace 10.0.1.50 with the actual pod IP
nc -zv 10.0.1.50 5432
# Expected if policy is working: nc: 10.0.1.50 (10.0.1.50:5432): Connection refused
# or it simply hangs and times out
# To test allowed traffic, run the test pod with the allowed label
kubectl run test-pod \
--image=busybox \
--rm -it \
--restart=Never \
--labels="app=payments-api" \
-n production \
-- sh
nc -zv 10.0.1.50 5432
# Expected: Connection to 10.0.1.50 5432 port [tcp/postgresql] succeeded!

Complete Production Setup — Layer by Layer

Bash
# Step 1: Apply default deny to the namespace
kubectl apply -f default-deny-all.yaml
# Step 2: Allow DNS so pods can resolve service names
kubectl apply -f allow-dns-egress.yaml
# Step 3: Allow Ingress controller → app pods
kubectl apply -f allow-ingress-controller.yaml
# Step 4: Allow app pods → database
kubectl apply -f allow-payments-api-to-db.yaml
# Step 5: Verify all policies are in place
kubectl get networkpolicies -n production
# Output:
# NAME POD-SELECTOR AGE
# default-deny-all <none> 5m
# allow-ingress-controller app=payments-api 4m
# allow-payments-api-to-db app=payments-db 3m
# allow-dns-egress app=payments-api 4m

Debugging NetworkPolicy Denials

NetworkPolicy denials are silent by default — a blocked connection just times out, no error message says "blocked by NetworkPolicy." This makes debugging hard.

Bash
# Step 1: Check what policies exist and what they select
kubectl get networkpolicies -n production -o wide
# Step 2: Describe a specific policy to see its full rules
kubectl describe networkpolicy allow-payments-api-to-db -n production
# Step 3: Check pod labels match the policy selectors
kubectl get pod payments-api-7d9f8b-xk2p9 -n production --show-labels
# Verify 'app=payments-api' label is present
# Step 4: If using Cilium, use its built-in observability
# kubectl exec -it -n kube-system cilium-xxxxx -- cilium monitor --type drop
# This shows real-time dropped packets with the reason

🔴 Common Mistake: Applying a NetworkPolicy with a podSelector that does not match any pods due to a label mismatch. The policy is active but protects nothing — all traffic still flows freely. After applying any policy, always run kubectl get networkpolicies -n <namespace> -o wide and verify the POD-SELECTOR column shows the labels you intended.

💡 Tip: At Hotstar or PhonePe scale, use Cilium instead of Calico if you're on a modern cluster. Cilium's network policies extend beyond port/IP rules to HTTP-layer policies — you can write rules like "allow only GET requests to /api/v1/products from the frontend namespace." This is called Layer 7 policy and it is far more expressive than standard Kubernetes NetworkPolicy.

Resources

Velero vs etcd Snapshot: Not a Real Choice

Velero vs etcd Snapshot: Not a Real Choice

Velero and etcd snapshots protect different layers of a cluster, not the same thing. Here's what each covers and why production DR needs both.

5 min read•Aug 2026
Istio Ambient vs Linkerd in 2026

Istio Ambient vs Linkerd in 2026

Istio Ambient killed the sidecar-tax argument. The real 2026 decision is waypoint topology and Buoyant's licensing shift, not features vs simplicity.

5 min read•Aug 2026
Nginx Ingress vs Traefik vs Gateway API in 2026

Nginx Ingress vs Traefik vs Gateway API in 2026

Ingress-nginx retired in March 2026. Here's how Traefik and the Gateway API actually compare as replacements — and why "just swap it" is the wrong frame.

5 min read•Aug 2026
OPA Gatekeeper vs Kyverno: Policy Engine in 2026

OPA Gatekeeper vs Kyverno: Policy Engine in 2026

OPA Gatekeeper vs Kyverno compared for 2026 - Rego vs YAML, mutation maturity, operational overhead, and which policy engine fits your cluster.

5 min read•Aug 2026
Helm vs Kustomize in 2026: Templating vs Patching

Helm vs Kustomize in 2026: Templating vs Patching

Helm vs Kustomize compared for 2026 - templating vs patching, Helm 4's new features, and why most production teams end up running both.

5 min read•Aug 2026
Prometheus vs Datadog vs New Relic: Real Costs

Prometheus vs Datadog vs New Relic: Real Costs

Prometheus, Datadog, and New Relic compared for 2026 - real pricing at scale, hidden cost drivers, and which fits a Kubernetes-heavy stack.

5 min read•Aug 2026
Cluster Autoscaler vs Karpenter for EKS in 2026

Cluster Autoscaler vs Karpenter for EKS in 2026

Cluster Autoscaler vs Karpenter compared for EKS in 2026 - provisioning speed, bin-packing, cloud support, and when each is the right default.

5 min read•Aug 2026
GKE vs EKS vs AKS in 2026: Which Fits Your Team?

GKE vs EKS vs AKS in 2026: Which Fits Your Team?

GKE, EKS, and AKS compared for 2026 - control plane pricing, Autopilot vs Karpenter vs Node Auto Provisioning, and which platform actually fits your team.

5 min read•Aug 2026
K3s vs K8s vs MicroK8s in 2026

K3s vs K8s vs MicroK8s in 2026

K3s, full Kubernetes, and MicroK8s compared for 2026 - resource footprint, production readiness, and which fits edge, homelab, or cloud workloads.

5 min read•Aug 2026
Canary Deployments with Argo Rollouts & Flagger

Canary Deployments with Argo Rollouts & Flagger

Ship to 5% of users first and auto-rollback in minutes — a hands-on guide to canary deployments with Argo Rollouts and Flagger on Kubernetes.

5 min read•Jun 2026
OpenTelemetry Explained: Metrics, Logs, Traces

OpenTelemetry Explained: Metrics, Logs, Traces

OpenTelemetry unifies metrics, logs, and traces under one open standard — how it works, what it replaces, and how to instrument a service in 20 minutes.

5 min read•Jun 2026
Kubernetes Cost Optimization Without Breaking SLOs

Kubernetes Cost Optimization Without Breaking SLOs

Average Kubernetes CPU utilization across production clusters is 8%. Here is the complete 2026 playbook for cutting cloud spend without touching your SLOs.

5 min read•Jun 2026
ArgoCD vs FluxCD: GitOps for Kubernetes in 2026

ArgoCD vs FluxCD: GitOps for Kubernetes in 2026

ArgoCD and FluxCD are the two dominant GitOps engines for Kubernetes in 2026 — this breakdown tells you exactly which one to pick and why.

10 min read•Jun 2026

Explore More in Kubernetes Networking and Traffic Management

All 6 Topics

Frequently Asked Questions

Is Kubernetes Network Policies for Pod-Level Traffic Control free to learn on DevOps Network?

Yes - this topic, like everything on DevOps Network, is 100% free with no paywall or sign-up gate.

What does the Kubernetes Network Policies for Pod-Level Traffic Control topic cover?

By default, every pod in a Kubernetes cluster can talk to every other pod — regardless of namespace, team, or sensitivity. Network Policies are Kubernetes firewall rules that restrict this. They define which pods are allowed to send traffic to which other pods, and which external IPs can reach your services. Without them, a compromised frontend pod can directly connect to your production database.