Egress gateways compared

Pods egress from whatever node they land on. Serverless functions egress from a pool the provider reshuffles. An egress gateway is the one exit you route everything through so the far side sees a single, predictable address.

There are four ways to get one. Three of them you operate yourself. This page covers all four, in the order you should consider them.

1. Istio egress gateway

If you already run Istio, the gateway is a deployment you already have. Force the traffic through it and give its nodes a fixed address at the cloud level.

ServiceEntry + VirtualService, the short version

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: partner-api
spec:
  hosts: ["api.partner.example"]
  ports:
    - { number: 443, name: tls, protocol: TLS }
  resolution: DNS
  location: MESH_EXTERNAL
---
apiVersion: networking.istio.io/v1
kind: Gateway
metadata:
  name: istio-egressgateway
spec:
  selector: { istio: egressgateway }
  servers:
    - port: { number: 443, name: tls, protocol: TLS }
      hosts: ["api.partner.example"]

The catch is that Istio routes the traffic but does not own an address. The gateway pods still egress via their node, so you also need the node pool pinned to a NAT gateway or a reserved address — which is step 4 below, with Istio in front of it.

A ServiceEntry alone does not force traffic through the gateway. Without outboundTrafficPolicy: REGISTRY_ONLY or an explicit sidecar rule, pods keep talking to the internet directly and the identity silently is not applied. This is the most common way an Istio egress setup looks correct and is not.

2. Cilium egress gateway

Cilium does own the address. An IsovalentEgressGatewayPolicy (or CiliumEgressGatewayPolicy in OSS) SNATs matching pod traffic to an address on a chosen node.

egress-policy.yaml

apiVersion: cilium.io/v2
kind: CiliumEgressGatewayPolicy
metadata:
  name: partner-sync
spec:
  selectors:
    - podSelector:
        matchLabels:
          io.kubernetes.pod.namespace: default
          app: partner-sync
  destinationCIDRs: ["0.0.0.0/0"]
  egressGateway:
    nodeSelector:
      matchLabels: { egress-node: "true" }
    egressIP: 10.0.1.50

Cleaner than Istio for this specific job, and it is a real gateway rather than routing rules. The constraints are that it needs Cilium as your CNI, the egress node becomes a single point of failure for everything matching the policy, and the address is still a cloud address bound to that node's subnet.

3. Calico egress gateway

Calico's egress gateway is a pod, not a node role, which makes it schedulable and easier to give per-namespace identities. It is an Enterprise/Cloud feature — Calico Open Source does not include it.

Same underlying constraint as Cilium: it decides which address inside your cloud the traffic leaves from, so you still have to own an address in that cloud, in that region.

4. Cloud NAT gateway

The provider's own answer, and the one their documentation sends you to. Put the workload in private subnets, route 0.0.0.0/0 at a NAT gateway, attach a reserved address.

It works, it needs no CNI features, and it is the most expensive of the four to keep running. On AWS a NAT gateway is about $32 a month per availability zone before data processing, charged per region and per VPC. Two AZs in two regions is four gateways for one identity. Azure NAT Gateway and Google Cloud NAT bill on the same shape.

What all four have in common

Every option above makes the address a property of the infrastructure that is using it. That is fine until one of these happens:

  • You move region or cloud. The address does not come with you, and every partner who allowlisted it has to be asked again.
  • A second cluster, a CI runner, or one laptop needs the same identity. Now you are peering networks to share an address.
  • The workload is serverless. There is no node to put a gateway on, so the answer becomes a VPC, a NAT gateway and a monthly bill larger than the thing it is fronting.

The address is doing an organisational job — being recognised by someone else — while being owned by a piece of infrastructure that you will replace. That mismatch is the actual problem, and none of the four options above fix it.

5. Rent the identity instead of operating the gateway

The alternative is to hold the address outside all of it. We assign you a dedicated IPv4 out of space we hold at RIPE and announce from our own AS, and deliver it to your machines over a WireGuard tunnel. Your traffic leaves as that address regardless of which cluster, cloud or region it originated in.

one pod, in any cluster, on any CNI

kubectl create secret generic aip-tunnel \
  --from-file=aip0.conf=./anchoredip-42.conf

There is no gateway to operate, no CNI requirement, no per-region multiplication, and moving clouds does not change the address. The Kubernetes guide has both patterns — a sidecar for one workload, or one egress deployment behind a Service for the whole cluster.

Where this is the wrong tool. If your traffic never leaves one cloud and one region, a NAT gateway you already have is simpler than adding a tunnel. If you need an address for traffic coming in — a load balancer clients connect to — this is the wrong shape entirely; you want an Elastic IP or a reserved address. This solves outbound identity only.

A real address out of the same range paying customers are in. No card, and nobody to talk to.

If you are weighing this against a static-IP proxy service, the differences are set out in AnchoredIP compared with QuotaGuard — including the cases where a proxy is the better fit.

Questions people ask first

What is an egress gateway?
A single node that outbound traffic is routed through so that it leaves with one predictable source address, instead of leaving from whichever machine happens to be running the workload. It exists because the thing on the other side wants to recognise you.
Do I need an egress gateway to get a static egress IP?
You need something that holds the address and that all your traffic passes through. That can be a gateway you run inside the cluster, a cloud NAT gateway, or a tunnel to an address held elsewhere. The requirement is one exit point, not a particular product.
What does a cloud NAT gateway cost?
On AWS a NAT gateway is roughly $32 a month per availability zone before any data processing charges, and it is per region, per VPC. Azure NAT Gateway and Google Cloud NAT are billed on the same shape. The address itself is a few dollars; the gateway around it is the bill.
Does the address survive rebuilding the cluster?
Only if it is not allocated by the cluster. An Elastic IP or a reserved address belongs to the cloud account and region it was created in, so moving clouds means a new address and a new conversation with everyone who allowlisted the old one.