Kubernetes Egress Network Policies
Control outbound traffic from pods with egress NetworkPolicies. Allow DNS, block internet access, and restrict pod-to-pod communication by namespace.
π‘ Quick Answer: Egress NetworkPolicies restrict outbound traffic from pods. Always allow DNS (port 53 to kube-dns) in egress rules, then whitelist specific destinations by CIDR, namespace, or pod labels.
The Problem
By default, pods can reach any IP β the internet, other namespaces, metadata services (169.254.169.254). In a zero-trust model, you need to explicitly allow only required outbound connections.
The Solution
Default Deny All Egress
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-deny-egress
namespace: production
spec:
podSelector: {}
policyTypes:
- EgressAllow DNS + Specific Services
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: app-egress
namespace: production
spec:
podSelector:
matchLabels:
app: web-api
policyTypes:
- Egress
egress:
# Allow DNS
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53
- protocol: TCP
port: 53
# Allow database in same namespace
- to:
- podSelector:
matchLabels:
app: postgres
ports:
- protocol: TCP
port: 5432
# Allow external API
- to:
- ipBlock:
cidr: 203.0.113.0/24
ports:
- protocol: TCP
port: 443Block Cloud Metadata Service
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: block-metadata
namespace: production
spec:
podSelector: {}
policyTypes:
- Egress
egress:
- to:
- ipBlock:
cidr: 0.0.0.0/0
except:
- 169.254.169.254/32Allow Traffic to Another Namespace
egress:
- to:
- namespaceSelector:
matchLabels:
name: monitoring
podSelector:
matchLabels:
app: prometheus
ports:
- protocol: TCP
port: 9090graph LR
P[Pod: web-api] -->|Port 53| DNS[kube-dns β]
P -->|Port 5432| DB[postgres β]
P -->|Port 443| API[External API β]
P -.->|Port 80| Internet[Internet β]
P -.->|Port 169.254| Meta[Metadata β]Common Issues
All DNS breaks after applying egress deny Always include DNS allow rule:
- to:
- namespaceSelector: {}
podSelector:
matchLabels:
k8s-app: kube-dns
ports:
- protocol: UDP
port: 53Policy not enforced NetworkPolicies require a CNI that supports them (Calico, Cilium, Weave). Flannel does NOT enforce policies:
kubectl get pods -n kube-system | grep -E "calico|cilium"Egress to headless service fails Use pod/namespace selectors instead of ipBlock for in-cluster services β pod IPs change.
Best Practices
- Always start with default-deny-egress per namespace
- Allow DNS first in every egress policy
- Use namespace selectors for in-cluster communication
- Use CIDR blocks for external services with stable IPs
- Block metadata endpoint (169.254.169.254) in multi-tenant clusters
- Test policies with
kubectl exec+curl/ncbefore enforcing - Use Ciliumβs
CiliumNetworkPolicyfor L7 (HTTP path/method) egress rules
Key Takeaways
- Egress policies control outbound traffic from selected pods
- An empty
egress: []in policyTypes blocks all outbound traffic - DNS (UDP/TCP 53) must be explicitly allowed when egress is restricted
ipBlockworks for external IPs; pod/namespace selectors for in-cluster- Multiple egress rules are ORed β matching any rule allows the traffic
- CNI must support NetworkPolicy (Calico, Cilium) β Flannel doesnβt enforce

Recommended
Kubernetes Recipes β The Complete Book100+ production-ready patterns with detailed explanations, best practices, and copy-paste YAML. Everything in one place.
Get the Book βLearn by Doing
CopyPasteLearn β Hands-on Cloud & DevOps CoursesMaster Kubernetes, Ansible, Terraform, and MLOps with interactive, copy-paste-run lessons. Start free.
Browse Courses βπ Deepen Your Skills β Hands-on Courses
Courses by CopyPasteLearn.com β Learn IT by Doing
