πŸ“šBook Signing at KubeCon EU 2026Meet us at Booking.com HQ (Mon 18:30-21:00) & vCluster booth #521 (Tue 24 Mar, 12:30-1:30pm) β€” free book giveaway!RSVP Booking.com Event
Security intermediate ⏱ 10 minutes K8s 1.21+

Kubernetes NetworkPolicy Default Deny Egress

Implement Kubernetes NetworkPolicy default deny egress rules. Block all outbound traffic, then allow specific destinations: DNS, external APIs.

By Luca Berton β€’ β€’ πŸ“– 5 min read

πŸ’‘ Quick Answer: Apply a NetworkPolicy with policyTypes: ["Egress"] and empty egress: [] to deny all outbound traffic. Then create additional policies to allow DNS (port 53), specific services, and required external endpoints. Always allow DNS first or nothing works.

The Problem

By default, pods can reach any destination β€” internal or external:

  • Compromised pods can exfiltrate data
  • Lateral movement between namespaces is unrestricted
  • No visibility into what pods are connecting to
  • Compliance requires explicit egress allowlists

The Solution

Default Deny All Egress

# Deny ALL outbound traffic for all pods in namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-egress
  namespace: production
spec:
  podSelector: {}  # Applies to ALL pods in namespace
  policyTypes:
    - Egress
  egress: []  # Empty = deny all

Allow DNS (Always Needed First)

# Without DNS, pods can't resolve any service names
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}  # All pods
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53

Allow Internal Service Communication

# Allow app pods to reach database in same namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: app-to-database
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
    - Egress
  egress:
    - to:
        - podSelector:
            matchLabels:
              app: postgres
      ports:
        - protocol: TCP
          port: 5432
---
# Allow app to reach Redis in another namespace
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: app-to-redis
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: cache
          podSelector:
            matchLabels:
              app: redis
      ports:
        - protocol: TCP
          port: 6379

Allow Specific External IPs

# Allow outbound to specific external API
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-external-api
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: payment-service
  policyTypes:
    - Egress
  egress:
    - to:
        - ipBlock:
            cidr: 203.0.113.0/24  # External payment API range
      ports:
        - protocol: TCP
          port: 443
    # Also allow DNS for resolution
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

Complete Zero-Trust Namespace Setup

# 1. Deny all ingress AND egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress
  ingress: []
  egress: []
---
# 2. Allow DNS for all pods
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-dns
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Egress
  egress:
    - to:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: kube-system
      ports:
        - protocol: UDP
          port: 53
        - protocol: TCP
          port: 53
---
# 3. Allow ingress from ingress controller
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-controller
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: myapp
  policyTypes:
    - Ingress
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

Architecture

graph TD
    A[Pod in Production NS] -->|default deny| B[❌ All Egress Blocked]
    
    A -->|allow-dns policy| C[βœ… CoreDNS :53]
    A -->|app-to-database| D[βœ… Postgres :5432]
    A -->|allow-external-api| E[βœ… Payment API :443]
    A -->|no policy| F[❌ Internet]
    A -->|no policy| G[❌ Other namespaces]

Common Issues

IssueCauseFix
All DNS resolution breaksForgot to allow DNS egressAdd DNS allow policy first
Pods can still reach everythingCNI doesn’t support NetworkPolicyUse Calico, Cilium, or Antrea
Cross-namespace traffic blockedMissing namespaceSelectorAdd namespace label selector
External API unreachableNeed both DNS + IP block allowAdd both in same policy
Health checks failLiveness probe from kubelet blockedkubelet isn’t subject to NetworkPolicy (host network)

Best Practices

  1. Apply deny-all FIRST, then allow specific β€” explicit allowlist approach
  2. Always allow DNS β€” first policy after deny-all, or nothing resolves
  3. Label namespaces β€” kubernetes.io/metadata.name is auto-applied since K8s 1.21
  4. Test with kubectl exec curl β€” verify connectivity after applying policies
  5. Use Cilium/Calico for FQDN egress rules β€” native NetworkPolicy only supports IPs

Key Takeaways

  • policyTypes: ["Egress"] with empty egress: [] = deny all outbound
  • DNS (UDP/TCP 53 to kube-system) must be explicitly allowed or nothing works
  • Policies are additive β€” multiple policies allowing different things all take effect
  • CNI must support NetworkPolicy (Calico, Cilium, Antrea) β€” kubenet/flannel don’t
  • Host-networked processes (kubelet probes) bypass NetworkPolicy β€” no need to allow
#networkpolicy #egress #deny #security #zero-trust
Luca Berton
Written by Luca Berton

Principal Solutions Architect specializing in Kubernetes, AI/GPU infrastructure, and cloud-native platforms. Author of Kubernetes Recipes and creator of CopyPasteLearn courses.

Kubernetes Recipes book cover

Want More Kubernetes Recipes?

This recipe is from Kubernetes Recipes, our 750-page practical guide with hundreds of production-ready patterns.

Luca Berton Ansible Pilot Ansible by Example Open Empower K8s Recipes Terraform Pilot CopyPasteLearn ProteinLens