Cheveo Blog
managed-kubernetes 11 min read ·

Kubernetes NetworkPolicy Cheatsheet: 12 Copy-Paste Patterns

NetworkPolicies are powerful but confusing. 12 production-ready YAML patterns for ingress, egress, namespace isolation and monitoring. Including the AND-vs-OR trap.

Clemens Christen
Clemens Christen Certified Kubernetes Administrator (CKA)

TL;DR - NetworkPolicies are Kubernetes-native firewall rules at pod level. Without them all traffic is allowed. 12 copy-paste patterns cover 90% of production cases: from deny-all defaults to Prometheus scraping and DNS egress. The biggest trap is the AND-vs-OR semantics on selectors.

🔖 Just want the YAMLs? Here’s the interactive NetworkPolicy cheatsheet - with a copy button per pattern and common mistakes. Bookmark recommended.

How NetworkPolicies work

The mental model in three sentences:

  1. Without a policy, everything is allowed. Any pod can talk to any pod in any namespace. That’s the Kubernetes default.
  2. Once a policy matches a pod, everything not explicitly allowed is blocked. A podSelector: {} matches all pods in the namespace.
  3. Policies are additive. Multiple policies on the same pod never contradict, they combine their allows.

Important: NetworkPolicies need a CNI plugin that enforces them. Cilium, Calico and Weave Net do. Default kubenet in some managed K8s setups silently ignores policies. That’s the sneakiest trap: you write policies, test on the cluster, everything still works, and you think “policy is active”. In reality nothing is enforced.

# Check CNI
kubectl get pods -n kube-system -l k8s-app=cilium
kubectl get pods -n kube-system -l k8s-app=calico-node

The AND-vs-OR trap

The most common NetworkPolicy confusion. Two selectors in a from: block behave differently depending on formatting:

OR (two separate list items):

ingress:
  - from:
      - namespaceSelector:          # list item 1
          matchLabels:
            name: monitoring
      - podSelector:                # list item 2
          matchLabels:
            app: prometheus

Means: traffic from all pods in namespace “monitoring” OR from all pods with label app=prometheus in the own namespace. Much more open than intended.

AND (one single list item):

ingress:
  - from:
      - namespaceSelector:          # part of list item 1
          matchLabels:
            name: monitoring
        podSelector:                # also part of list item 1
          matchLabels:
            app: prometheus

Means: traffic only from pods with label app=prometheus that are in namespace “monitoring”. That’s usually what you want.

The difference is two characters in the YAML: the dash before podSelector. With dash: OR. Without: AND.

The 12 patterns

Deny defaults (the foundation)

Every zero-trust approach starts with a deny-all. Block everything first, then explicitly allow.

Pattern 1: Deny all ingress

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-ingress
  namespace: <ns>
spec:
  podSelector: {}
  policyTypes:
    - Ingress

Blocks all incoming traffic to all pods in the namespace. No ingress: block = no exceptions.

Pattern 2: Deny all egress

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all-egress
  namespace: <ns>
spec:
  podSelector: {}
  policyTypes:
    - Egress

Blocks all outgoing traffic. Caution: DNS is also blocked. Pods can’t resolve service names. Always combine with pattern 10 (DNS egress).

Pattern 3: Deny all (both)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: deny-all
  namespace: <ns>
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

Complete isolation. Namespace is silent. Open patterns one by one from here.

Namespace and pod access

Pattern 4: Allow from same namespace

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-same-namespace
  namespace: <ns>
spec:
  podSelector: {}
  ingress:
    - from:
        - podSelector: {}
  policyTypes:
    - Ingress

All pods in the namespace can reach each other, but nothing from outside. The most common pattern for internal microservices.

Pattern 5: Allow from specific namespace

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-namespace
  namespace: <ns>
spec:
  podSelector: {}
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: backend
  policyTypes:
    - Ingress

Allows traffic from a specific namespace (here: backend). Prerequisite: the source namespace must have a matching label (kubectl label namespace backend name=backend).

Pattern 6: Allow specific port only

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-http-only
  namespace: <ns>
spec:
  podSelector:
    matchLabels:
      app: web
  ingress:
    - ports:
        - port: 8080
          protocol: TCP
      from:
        - podSelector: {}
  policyTypes:
    - Ingress

Only port 8080/TCP, only from the own namespace. Everything else blocked.

1-Day Intensive Workshop

Kubernetes Debugging - systematic, not guesswork

Replay real production incidents, internalise kubectl workflows, find root causes in minutes.

View workshop details

Infrastructure access

Pattern 7: Allow from ingress controller

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-from-ingress
  namespace: <ns>
spec:
  podSelector:
    matchLabels:
      app: web
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
  policyTypes:
    - Ingress

Allows inbound traffic from the ingress controller namespace. Without this pattern your app is unreachable from outside, even if the Ingress is correctly configured. Common labels: ingress-nginx, traefik, istio-ingress.

Pattern 8: Allow Prometheus scraping (AND variant)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-prometheus
  namespace: <ns>
spec:
  podSelector: {}
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              name: monitoring
          podSelector:
            matchLabels:
              app.kubernetes.io/name: prometheus
      ports:
        - port: 9090
          protocol: TCP
  policyTypes:
    - Ingress

AND variant: only Prometheus (label app.kubernetes.io/name: prometheus) from the monitoring namespace. Adjust the port to your app’s metrics port.

Pattern 9: Allow from specific pod (database access)

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-backend-to-db
  namespace: <ns>
spec:
  podSelector:
    matchLabels:
      app: database
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: backend
      ports:
        - port: 5432
          protocol: TCP
  policyTypes:
    - Ingress

Only the backend pod may access the database, only on port 5432.

Egress control

Pattern 10: Allow DNS egress only

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns-egress
  namespace: <ns>
spec:
  podSelector: {}
  egress:
    - ports:
        - port: 53
          protocol: UDP
        - port: 53
          protocol: TCP
  policyTypes:
    - Egress

The most important egress pattern. Without DNS egress, pods can’t resolve service names: curl http://api-service:8080 fails even though the pod is technically reachable. Always combine with deny-all-egress (pattern 2 or 3).

Pattern 11: Allow egress to external CIDR

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-egress-to-db
  namespace: <ns>
spec:
  podSelector:
    matchLabels:
      app: backend
  egress:
    - to:
        - ipBlock:
            cidr: 10.0.0.0/24
      ports:
        - port: 5432
          protocol: TCP
    - ports:
        - port: 53
          protocol: UDP
  policyTypes:
    - Egress

Backend may only reach an external database (subnet 10.0.0.0/24, port 5432) and DNS. For RDS, Cloud SQL, or managed DB instances outside the cluster.

Pattern 12: Allow egress to internal service

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
  namespace: <ns>
spec:
  podSelector:
    matchLabels:
      app: frontend
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: api
      ports:
        - port: 8080
          protocol: TCP
    - ports:
        - port: 53
          protocol: UDP
  policyTypes:
    - Egress

Frontend may only talk to the API service (port 8080) and DNS.

The 5 most common mistakes

  1. Forgetting DNS egress. Deny-all-egress without DNS allow (pattern 10) kills all connections, even though ingress policies are correct.
  2. Confusing AND and OR. One dash difference in the YAML opens access far wider than intended. Always test explicitly.
  3. Missing namespace labels. namespaceSelector matches on namespace labels, not on the name. Set labels manually: kubectl label namespace <ns> name=<ns>.
  4. CNI doesn’t enforce. Policies exist in the API but kubenet ignores them. kubectl get pods -n kube-system | grep cilium to verify.
  5. Policies only on ingress, forgot egress. Ingress policies alone don’t control outgoing traffic. Zero-trust needs both.

Where to go from here

In the Kubernetes Debugging Workshop we simulate an incident where a missing NetworkPolicy causes unintended cross-namespace traffic, and debug it systematically.

Related from our series:

1-Day Intensive Workshop

Kubernetes Debugging - systematic, not guesswork

Replay real production incidents, internalise kubectl workflows, find root causes in minutes.

View workshop details
Free · 30 minutes

Need a second opinion on your cluster?

Book a free 30-minute Kubernetes health check. We review your setup and give concrete recommendations, no sales pitch.

Book a slot