Outbound connectivity is the part of Azure networking nobody designs until it breaks, and it breaks in one specific way: SNAT port exhaustion, where connections to external services start failing intermittently under load while every dashboard shows green. Today we make outbound deliberate.
The End of Accidental Egress
Historically, a VM with no public IP and no load balancer still got default outbound access through an implicit, Microsoft managed SNAT IP. That implicit path is deprecated for new deployments: new subnets should be created with explicit outbound methods, and the platform is retiring default outbound access precisely because an egress path you did not choose, with an IP you cannot predict or allowlist, is a liability. Your options, in order of preference for most designs: route egress through the hub firewall (the enterprise pattern from the earlier series), attach a NAT Gateway to the subnet, or use load balancer outbound rules. NAT Gateway is the right answer whenever you need high scale, predictable source IPs without full firewall inspection.
Understanding SNAT Exhaustion
Every outbound connection through a shared public IP consumes a SNAT port, a tuple of source IP and port that maps return traffic. A single public IP offers roughly 64,000 ports, and with load balancer outbound rules those ports are pre allocated per instance: 1,024 ports each for a backend pool of up to 50 VMs, whether an instance needs them or not. A chatty application opening many short lived connections to the same destination burns through its allocation, and the failure mode is silent connection drops with retries that make it worse. NAT Gateway changes the model: ports are allocated dynamically on demand from the whole pool, up to 64,512 per public IP with up to 16 IPs attached, over a million ports, and idle connections are reclaimed on a configurable timer. Exhaustion stops being an architecture problem and becomes a capacity dial.
resource "azurerm_public_ip_prefix" "egress" {
name = "ippre-egress-weu"
location = "westeurope"
resource_group_name = azurerm_resource_group.net.name
prefix_length = 30
zones = ["1"]
}
resource "azurerm_nat_gateway" "this" {
name = "natgw-apps-weu"
location = "westeurope"
resource_group_name = azurerm_resource_group.net.name
sku_name = "Standard"
idle_timeout_in_minutes = 4
zones = ["1"]
}
resource "azurerm_nat_gateway_public_ip_prefix_association" "this" {
nat_gateway_id = azurerm_nat_gateway.this.id
public_ip_prefix_id = azurerm_public_ip_prefix.egress.id
}
resource "azurerm_subnet_nat_gateway_association" "apps" {
subnet_id = azurerm_subnet.apps.id
nat_gateway_id = azurerm_nat_gateway.this.id
}
Use a public IP prefix rather than individual IPs: partners allowlist one CIDR block, and you can grow within it without emailing every third party you integrate with. Keep the idle timeout at the default 4 minutes unless you have long lived idle connections that genuinely need more, because longer timeouts hold ports hostage. Note the zonal deployment: a NAT gateway is a zonal resource, so the zone resilient pattern is one NAT gateway per zone with zonal subnets, or accept the single zone dependency consciously.
How It Composes
Precedence matters and it is simple: NAT Gateway on a subnet wins over load balancer outbound rules and over instance level public IPs for outbound flows. That makes migration clean, attach the NAT gateway and outbound moves to it immediately, no VM changes. With a hub firewall design, the choice is per subnet: workloads whose egress must be inspected route 0.0.0.0/0 to the firewall, while high volume trusted egress (build agents pulling packages, AKS pulling images through a dedicated subnet) can take the NAT gateway path and spare the firewall the throughput. On AKS specifically, outboundType userAssignedNATGateway hands the cluster’s egress to your NAT gateway and ends the era of SNAT exhaustion tickets from nodes hammering an external API.
Monitor two metrics: SNAT connection count against your port capacity, and dropped packets. Alert well before the ceiling, adding a public IP to the prefix takes a minute; diagnosing intermittent egress failures without metrics takes a week of someone’s life.
Cheers
Osama
Leave a comment