Configuring Egress Gateway with Static Node IP
This guide shows how to route outbound traffic from pods through a specific Kubernetes node using Cilium’s Egress Gateway functionality. This is useful when you need predictable egress IPs, for example, for firewall allowlists or compliance.
Warning
This guide assumes your egress gateway node has a static public IP assigned to the host. On Hetzner, cloud VMs will receive a different public IP after cluster upgrades or node reprovisioning. To avoid this, use bare-metal servers as your egress gateway nodes.
As an added benefit, Hetzner bare-metal machines offer significantly higher network bandwidth: up to 10 Gbit/s dedicated NIC, compared to max 1 Gbit/s on cloud instances.
Step 1: Label the Egress Gateway Node#
The label acts as a selector that the Egress Gateway Policy will use to identify which node should handle the outbound traffic. Without this label, the policy wouldn’t know which node is eligible to route egress traffic, and the setup would not function as intended.
For adding a persistent label to the node, add it to the metadata section of your machine deployment in the Cluster resource. This ensures that the label is applied to the node even after a node replacement or upgrade. Example cluster definition:
apiVersion: cluster.x-k8s.io/v1beta2
kind: Cluster
metadata:
name: egress-testing
spec:
clusterNetwork:
services:
cidrBlocks: ["10.128.0.0/12"]
pods:
cidrBlocks: ["192.168.0.0/16"]
serviceDomain: "cluster.local"
topology:
classRef:
name: hetzner-apalla-1-35-v4
version: v1.35.7
controlPlane:
replicas: 1
workers:
machineDeployments:
- class: workeramd64hcloud
name: md-cloud
replicas: 3
failureDomain: nbg1
variables:
overrides:
- name: workerMachineTypeHcloud
value: cpx42
- class: workeramd64baremetal
name: md-baremetal
replicas: 1
metadata:
labels:
node-role.kubernetes.io/egress-gateway: "true"
variables:
- name: region
value: nbg1
- name: controlPlaneMachineTypeHcloud
value: cpx42
For more information on node labeling, please refer to the Labelling and assign roles on nodes page.
Step 2: Create the Egress Gateway Policy#
The Egress Gateway Policy is a Kubernetes resource that defines how outbound traffic should be routed. It specifies the node label to match and the static IP address to use for egress traffic.
Replace the egressIP value with the actual external IP of your egress gateway node. For finding the external IP of your node, you can use the following command:
$ kubectl get nodes -l node-role.kubernetes.io/egress-gateway=true -o jsonpath='{.items[0].status.addresses[?(@.type=="ExternalIP")].address}'
Sample Egress Gateway Policy YAML:
apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
name: force-egress-via-node
spec:
selectors:
- podSelector:
matchLabels:
app: my-app
destinationCIDRs:
- 0.0.0.0/0
egressGateway:
nodeSelector:
matchLabels:
node-role.kubernetes.io/egress-gateway: "true"
egressIP: 135.119.210.110
You can save this YAML to a file named egress-gateway-policy.yaml and apply it using kubectl:
$ kubectl apply -f egress-gateway-policy.yaml
In this example, the policy will route all outbound traffic from pods with the label app: my-app through the node labeled with node-role.kubernetes.io/egress-gateway: "true". There are additional options available for an Egress Gateway Policy, such as specifying multiple node labels or CIDR ranges. For more details, refer to the Cilium documentation.
Step 3: Test the Egress Gateway (Optional)#
To verify that the egress traffic is being routed through the specified node, you can deploy a test pod in the non-egress node and check its outbound IP address:
apiVersion: v1
kind: Pod
metadata:
name: egress-test
labels:
app: my-app
spec:
affinity:
nodeAffinity:
requiredDuringSchedulingIgnoredDuringExecution:
nodeSelectorTerms:
- matchExpressions:
- key: node-role.kubernetes.io/egress-gateway
operator: DoesNotExist
containers:
- name: curl
image: curlimages/curl:8.5.0
command: ["sleep", "3600"]
restartPolicy: Never
Save this YAML to a file named test-pod.yaml and apply it using kubectl:
$ kubectl apply -f test-pod.yaml
Once the pod is running, you can exec into it and check the outbound IP address. The following command uses ifconfig.me, a simple web service that returns the public IP address of the client making the request:
$ kubectl exec -it egress-test -- curl -s ifconfig.me
The output will show the IP address as seen by external services.
If you see the static IP address you specified in the Egress Gateway Policy, it means the setup is working correctly.