This episode covers Calico egress access control: egress gateway with a fixed IP, outbound NAT for traffic from pods to external networks, and the practice of securing access to external databases and source IP whitelists.

Every time a pod calls an external API, that traffic leaves the cluster. The question is: with what source IP? The default answer is the node IP — inconsistent, and it often gets external databases to reject requests because the whitelist doesn't match. Episode 10 solves this problem with Calico's egress control.
There are two main mechanisms we'll learn: an egress gateway to force traffic out through specific nodes with a fixed IP, and policy-based outbound access control so pods can only reach permitted destinations.
Pod egress traffic goes through the node where the pod runs, then gets NATed to the node IP. Because pods can move between nodes, the source IP changes. External services that use an IP allowlist will reject access from time to time, and the audit trail on the receiving side becomes useless.
Calico provides EgressGatewayPolicy: traffic from selected workloads is forced out through specific nodes (egress gateways) and NATed with a fixed IP. From the receiver's perspective, all requests appear to come from one stable IP.
An EgressGatewayPolicy selects source workloads with a selector, determines the egress IP, and limits which destinations may be accessed:
apiVersion: projectcalico.org/v3
kind: EgressGatewayPolicy
metadata:
name: database-egress
namespace: payments
spec:
selector: app == 'api'
egressIPs:
- 203.0.113.20
namespaceSelector: project == 'payments'
dst:
- networks:
- 198.51.100.0/24
ports:
- 5432Interpretation: pods labeled app: api in namespaces labeled project: payments may exit to the 198.51.100.0/24 network on port 5432, and that traffic leaves with IP 203.0.113.20.
When this policy is applied, Calico adds rules on the source node: packets matching the selector are directed to the egress gateway node (via IP-in-IP or a dedicated route), NATed there with the egress IP, then forwarded to the destination:
pod api -> policy matches -> route to the gateway node
-> NAT to 203.0.113.20 -> external network -> databaseExternal databases usually restrict connections to a list of IPs. With an egress gateway, that list only contains your egress IP. Apply a policy for the database port:
apiVersion: projectcalico.org/v3
kind: EgressGatewayPolicy
metadata:
name: db-prod
namespace: payments
spec:
selector: app == 'api'
egressIPs:
- 203.0.113.21
dst:
- networks:
- 198.51.100.0/24
ports:
- 5432The equally important side: combine this with a NetworkPolicy so only specific pods can use this egress. Outbound access policies and egress gateways work in layers — the gateway determines the exit IP, the policy determines who is allowed to exit.
To prove traffic leaves from the egress IP, check the routes and NAT on the node:
kubectl get egressgatewaypolicies -n payments
calicoctl get workloadendpoints -n payments -o wide
kubectl exec -n payments deploy/api -- sh -c \
"curl -s https://api.external.example.com/whoami | grep -E 'ip|address'"An echo service like whoami will show the source IP. If it shows 203.0.113.21 or similar, the egress gateway is working correctly. Cross-check with kubectl get egressgatewaypolicies -n payments to confirm the policy is registered in the cluster.
The egress gateway controls the exit IP, but to control who may exit where, keep using NetworkPolicy. Here's an example restricting pod egress to the database subnet only:
apiVersion: projectcalico.org/v3
kind: NetworkPolicy
metadata:
name: api-egress-db
namespace: payments
spec:
selector: app == 'api'
types:
- Egress
egress:
- action: Allow
protocol: TCP
destination:
nets:
- 198.51.100.0/24
ports:
- 5432Note destination.nets — this targets an external IP network, not a pod selector.
Don't forget to close other outbound access. Install a deny egress policy for the workload, then open only the destinations you need (including DNS). The complete pattern:
calicoctl apply -f api-default-deny.yaml
calicoctl apply -f api-egress-db.yaml
calicoctl apply -f egress-gateway-db.yaml
calicoctl get networkpolicy -n paymentsThe order is intentional: deny first, open destinations, then assign the exit IP. That way there's no uncontrolled egress gap.
Episode 10 closes the egress topic: a stable source IP with the egress gateway, port whitelists on the receiving side, and control of the outbound direction with NetworkPolicy combined into layered defense.
Key takeaways:
egressIPs, selector, and dst are the three main ingredients of EgressGatewayPolicy.In the next episode, episode 11, we cover DNS and FQDN policy — domain-based egress control for external service integration, combining DNS and IP policies in application policies, and the common pitfalls when using domains.