Why this matters
Azure bills outbound traffic in two places: the bytes that leave the virtual network and the service that sends them. NAT Gateway has an hourly charge and a data processing charge. It keeps charging while workloads are idle. A subnet with its own gateway can look tidy in a diagram and still cost more than the workload it serves.
Path sprawl is the bigger leak. A team adds a NAT Gateway for one subnet, a public IP on a jump box, an outbound rule on a Standard Load Balancer, and Azure Firewall for inspection. Traffic leaves through different paths at different times. You pay for idle gateways, public IPs, and every gigabyte that crosses the internet. Retries and failed connections add bytes too.
Private endpoints and service endpoints change the path. They keep traffic to Azure Storage, Key Vault, SQL, or Container Registry on private addresses or the Azure backbone. That removes NAT processing for those calls. It adds endpoint charges, so compare both sides. The goal is not zero gateways. It is the fewest gateways on the right subnets, with chatty traffic kept private.
Before you start
Collect the current picture before you change routes. In the Azure portal, open Virtual networks, then each subnet, and note the route table and NAT gateway. Open NAT gateways and list every public IP and subnet association. Check Public IP addresses for unattached or VM attached addresses. Check Load balancers for outbound rules.
You need Reader on the subscriptions, Cost Management Reader, and Monitoring Reader. Network Contributor is needed to change route tables, subnets, and gateways. Do not start with production if you cannot watch it. Pick a low risk subscription or a maintenance window. Export current route tables and NAT associations so you can roll back.
Get a cost baseline in Microsoft Cost Management. Group by service name and meter, then filter for NAT Gateway and Data Transfer. Group by tag if your team tags workloads. In Azure Monitor, open NAT Gateway metrics and note Data processed, SNAT connection count, and dropped packets for the last two weeks. Enable VNet flow logs on one busy VNet if you need top talkers. Flow logs have their own cost, so set a short retention.
Write down what must keep working: inbound public access, partner IP allow lists, outbound SaaS endpoints, and backup replication. Note any rule that requires inspection through Azure Firewall. A cheaper path that skips inspection is not a saving if it breaks a control.
How to do it
Start with the outbound path, not the invoice. Every subnet has an effective route for 0.0.0.0/0. It may point to the internet, to a NAT Gateway, to a firewall virtual appliance, or to a load balancer outbound rule. Draw one line per subnet. Mark where traffic is inspected, where it is source translated, and where it leaves Azure. That drawing shows duplicate gateways faster than a cost report.
Consolidate where the topology allows. A NAT Gateway can serve several subnets in the same virtual network, so a gateway per subnet is often waste. Keep separate gateways only for separate public IPs, separate SNAT pools, or a boundary between environments. Watch SNAT port use before you move busy subnets together. If the shared gateway runs out of ports, connections fail and retries add data processed.
Move Azure service traffic off the public path. Private endpoints for Storage, Key Vault, SQL, and Container Registry keep that traffic on private IPs. Service endpoints are simpler for some services and keep traffic on the Azure backbone. Both have trade-offs. Private endpoints add endpoint and data processing charges, and they need DNS changes. Test name resolution from each subnet before you cut over.
Tune the traffic that still goes out. Cache package and container pulls, compress logs before shipping, batch small API calls, and pool connections. Fix retry storms with backoff and jitter. A service that retries a failed call several times sends more bytes and opens more SNAT connections. Set NAT Gateway idle timeout and TCP keepalive to match your longest idle flow. Short timeouts create reconnects. Long ones hold ports.
- Map every outbound path. List each subnet, its route table, its NAT gateway, and any public IP or load balancer outbound rule. Use the effective routes view for a VM in each subnet. Mark the path that actually carries internet traffic.
- Remove idle gateways first. Find NAT gateways with low Data processed and no active subnet association. Confirm with the owning team, then delete the gateway and its unused public IPs. This is the lowest risk saving because no live path changes.
- Share gateways across subnets. Associate additional subnets with an existing NAT Gateway in the same virtual network when they share the same egress needs. Check SNAT connection count and dropped packets after each move. Keep separate gateways for environments that need separate public IPs.
- Put private endpoints in front of chatty services. Create private endpoints for Storage, Key Vault, SQL, or Container Registry in the same region. Update private DNS zones and test resolution from each subnet. Remove the public path only after the private path works.
- Clean up public IPs and outbound rules. Find VMs with public IPs that exist only so the VM can reach the internet. Move that subnet to NAT Gateway or a firewall egress path, then remove the public IP. Review Standard Load Balancer outbound rules and keep only the ones that serve a real need.
- Reduce repeated internet pulls. Mirror public container images into Azure Container Registry and pull over a private endpoint. Run an internal package mirror for OS and language packages. Compress and batch telemetry before it leaves the network.
- Set budgets, then retest and clean up. Create a budget in Microsoft Cost Management filtered to NAT Gateway and Data Transfer meters. Alert on daily actual spend and on a forecast threshold. After one clean week, delete old route tables, public IPs, and gateways and record the final diagram in your runbook.
How to check it worked
In Microsoft Cost Management, open the daily view and filter by meter. NAT Gateway data processed should fall if you moved service traffic to private endpoints or removed duplicate gateways. Data Transfer should fall if you cut repeated pulls or stopped cross region hops. Look at the trend over a full billing cycle, not one day.
In Azure Monitor, open NAT Gateway metrics. Check Data processed, SNAT connection count, and dropped packets. Dropped packets or a SNAT count near the limit mean the shared gateway is too small for the subnets you moved. Check application logs for connection timeouts and DNS errors after the change.
Use VNet flow logs for a short window if the bill did not move. Sort by destination and bytes to find the top talkers. Look for traffic to public storage endpoints, container registries, and telemetry services that should be private. Check that no VM still has a public IP that bypasses your intended path.
Confirm the security path too. If traffic was supposed to go through Azure Firewall, check firewall logs for the same flows. A lower NAT bill with uninspected traffic is a bad trade. Keep the old route table export until you have one clean week of metrics and no application errors.
Mistakes to avoid
Do not delete a NAT Gateway before you change the subnet association or route table. Outbound connections will fail and the fix is not obvious to every on call engineer. Move subnets in a maintenance window and watch dropped packets. Keep a rollback route table ready.
Do not put every subnet on its own gateway because the diagram looks simpler. That creates idle hourly charges and more public IPs. Share a gateway when the egress needs match, and split only for real boundaries.
Do not assume private endpoints are free or that they remove all egress. They add endpoint and data processing charges, and DNS mistakes cause outages. Compare the endpoint cost against the NAT and egress cost for the same service. For low volume services, a public endpoint with filtering may be cheaper.
Do not assume a compute commitment covers NAT Gateway. Check the meter and the commitment scope first. If the outbound picture is messy and the bill is large, the Cloud cost audit from Cloudverg is a fixed scope review at $4,500/month, and it is one option next to your own cleanup or a network consultant. Do the route map first either way.
Frequently asked questions
Why does an idle NAT Gateway cost money on Azure?
Azure bills NAT Gateway as a managed resource with an hourly charge, plus a charge for data processed. The hourly charge runs while the gateway exists, even if no subnet sends traffic through it. Delete gateways that no subnet uses and remove their public IPs.
Does a private endpoint remove NAT Gateway charges?
It removes that service traffic from the NAT path, so you process fewer gigabytes. Private endpoints have their own hourly and data processing charges. Compare both sides for each service, and keep DNS correct so traffic does not fall back to the public endpoint.
Can I share one NAT Gateway across several subnets?
Yes, a NAT Gateway can be associated with multiple subnets in the same virtual network. Check SNAT connection count and dropped packets after you add busy subnets. Use separate gateways when you need separate public IPs or separate environments.