INSIGHTS

Azure egress costs, explained: what is free and what is billed

Which Azure data transfer is free, which is billed per GB, and where the hidden meters sit: peering, NAT Gateway, Private Link and firewalls. With queries.

Patrick Rezende

·

Azure egress cost is what you pay when data leaves a boundary Azure meters. Inbound traffic is free, and so is traffic inside a region, including across availability zones. You pay for internet egress after the first 100 GB a month, for transfer between regions, and for VNet peering at both ends. NAT Gateway, Private Link, Azure Firewall and VPN gateways add their own per-GB processing charges on top.

That last group is the part most teams miss. "Egress" in casual conversation means bytes going to the internet. On an Azure invoice, the network line is usually a mix of five or six meters spread across several service names, and the internet piece is often not the largest.

What is free and what is billed?

List prices as of October 2026, pay-as-you-go, source region in North America or Europe. Other continents cost more.

Traffic

Charge

Notes

Inbound to Azure

Free

Any volume

Within a virtual network

Free


Between services in the same region

Free

VM to Azure SQL Database in the same region, for example

Across availability zones in a region

Free

Since Microsoft's May 2024 announcement

Azure origin to Azure Front Door (Standard/Premium)

Free

Front Door then bills its own delivery

Internet egress, first 100 GB per month

Free

All regions

Internet egress, next 10 TB

$0.087 per GB

Microsoft network routing. Routing preference via transit ISP lists at $0.08

Between regions in North America (or within Europe)

$0.02 per GB

Charged on the outbound side

From North America or Europe to another continent

$0.05 per GB


VNet peering, same region

$0.01 per GB out + $0.01 per GB in

Both ends pay

Global VNet peering, between Zone 1 regions

$0.035 per GB out + $0.035 per GB in

Varies by billing zone

NAT Gateway

$0.045 per hour + $0.045 per GB processed

Outbound and return traffic; internet egress still applies

Private endpoint

$0.01 per hour + $0.01 per GB processed, each direction

Data transfer still applies where relevant

Azure Firewall Standard

$0.016 per GB processed

Plus deployment hours

VPN gateway, VNet to VNet across regions

$0.035 per GB from Zone 1

Same-region VNet-to-VNet is free

Geo-redundant storage writes

Geo-replication data transfer, per GB

Applies to GRS, RA-GRS, GZRS, RA-GZRS

Two of these deserve a sentence each. The availability zone row is the one people still argue about: Microsoft planned to charge for cross-zone traffic, then announced on 21 May 2024 that it would not, for private or public IPs. And the "billing zone" in the peering row is a pricing grouping of regions (Zone 1 covers North America and Europe), unrelated to availability zones.

Where does egress hide on the bill?

In Microsoft Cost Management, network transfer is split across service names. If you only filter on "Bandwidth" you see part of it.

To get the full picture for last month:

  1. Open Cost analysis at subscription or billing scope and select last month.

  2. Group by Service name. Note the totals for Bandwidth, Virtual Network, NAT Gateway, Azure Firewall and VPN Gateway.

  3. Filter to each of those services and group by Meter. Peering shows as intra-region or inter-region ingress and egress meters. Private Link shows as data processed meters. Internet egress shows as data transfer out.

  4. Group the same view by Resource to see which gateway, firewall or VNet carries the volume.

  5. For storage accounts, check the Storage service for a geo-replication data transfer meter.

If an inter-availability-zone data transfer meter ever shows a non-zero charge, raise it with Microsoft support.

Cost Management tells you which meter and which resource, but not which workload sent the bytes. For that you need VNet flow logs, optionally with traffic analytics. Both are billed per GB (flow logs include 5 GB a month free), and if you send them to Log Analytics they become ingestion, so scope them to the subnets you are investigating. The Log Analytics cost guide covers keeping that ingestion under control.

How much does a hub-and-spoke design add per GB?

Hub-and-spoke is the default landing zone design, and it is a sensible one. It also means traffic between two spokes crosses peering twice and the firewall once.

Example, using the October 2026 list prices above, for two application spokes in the same region exchanging 50 TB a month:

Path

Per GB

50 TB a month

Spokes peered directly

$0.01 out + $0.01 in = $0.02

$1,000

Spoke to hub, through Azure Firewall, hub to spoke

2 × $0.02 peering + $0.016 firewall = $0.056

$2,800

Neither number will sink a company spending $100K a month on Azure. The point is that the second one grows linearly with traffic and appears on nobody's dashboard. Usually it starts as an architecture decision ("all east-west traffic through the firewall") that was right for security review and never priced. Some flows need inspection. A backup job copying terabytes between two spokes every night probably does not.

Private endpoints have their own rule. When you reach a private endpoint from a peered network, Microsoft charges the Private Link processing fee but not the peering fee. Centralizing private endpoints in the hub does not double-bill you for peering.

Why does NAT Gateway show up as an egress cost?

A NAT Gateway gives a subnet a fixed outbound IP. It bills hourly from creation (even with no subnet attached), and it bills $0.045 per GB for everything that passes through it, outbound and return. The internet egress charge is separate and still applies.

That processing rate is roughly half the first-tier internet egress rate. Example: 20 TB a month leaving East US to the internet through a NAT Gateway costs about $1,690 in internet egress and about $900 in NAT processing, plus around $33 of gateway hours. The NAT line is what nobody budgeted.

Common sources of large NAT volume in mid-market tenants: container images pulled from public registries on every node scale-out, application logs and traces shipped to a third-party SaaS, and backups or exports sent to another cloud. Most have a cheaper path. Pull images from Azure Container Registry over a private endpoint so they never touch the NAT Gateway. Send telemetry over private connectivity where the vendor offers it, or sample it. Bulk copies to another cloud pay egress whichever path they take, so the lever there is volume: send deltas, not full copies.

Which changes actually reduce egress?

In rough order of payback:

  • Put chatty pairs in the same region. An app in one region calling a database in another (often because of quota at deployment time) pays inter-region transfer on every response.

  • Serve static and cacheable content from Azure Front Door or a CDN. Origin to Front Door is free; you pay Front Door's delivery rates instead of serving every byte from the origin.

  • Compress responses and exports. Gzip on API responses and Parquet instead of CSV for data exports both cut billable bytes, and the code change is small.

  • Review GRS on high-write storage accounts. Every write to a geo-redundant account is replicated to the paired region and billed as geo-replication transfer. Keep it where the recovery objective needs it.

  • Route spoke-to-spoke traffic through the firewall only when it needs inspection. Use direct peering for bulk flows that security has signed off.

  • Check the ExpressRoute data plan. On a metered plan, outbound transfer is billed per GB; the unlimited plan includes it for a higher port fee. Compare the two against last quarter's actual outbound volume.

What network resources bill even with no traffic?

Some network resources charge by the hour whether or not traffic flows. These Azure Resource Graph queries find two common ones. Run them in Resource Graph Explorer.

// NAT gateways not attached to any subnet (hourly fee still applies)
resources
| where type =~ 'microsoft.network/natgateways'
| where array_length(properties.subnets) == 0 or isnull(properties.subnets)
| project name, resourceGroup, location, subscriptionId
// NAT gateways not attached to any subnet (hourly fee still applies)
resources
| where type =~ 'microsoft.network/natgateways'
| where array_length(properties.subnets) == 0 or isnull(properties.subnets)
| project name, resourceGroup, location, subscriptionId
// NAT gateways not attached to any subnet (hourly fee still applies)
resources
| where type =~ 'microsoft.network/natgateways'
| where array_length(properties.subnets) == 0 or isnull(properties.subnets)
| project name, resourceGroup, location, subscriptionId
// VPN and ExpressRoute gateways with their connection count
resources
| where type =~ 'microsoft.network/virtualnetworkgateways'
| project gwId = tolower(id), name, resourceGroup,
    gatewayType = tostring(properties.gatewayType),
    sku = tostring(properties.sku.name)
| join kind=leftouter (
    resources
    | where type =~ 'microsoft.network/connections'
    | project gwId = tolower(tostring(properties.virtualNetworkGateway1.id))
    | summarize connections = count() by gwId
  ) on gwId
| project name, resourceGroup, gatewayType, sku, connections = coalesce(connections, 0)
| order by connections asc
// VPN and ExpressRoute gateways with their connection count
resources
| where type =~ 'microsoft.network/virtualnetworkgateways'
| project gwId = tolower(id), name, resourceGroup,
    gatewayType = tostring(properties.gatewayType),
    sku = tostring(properties.sku.name)
| join kind=leftouter (
    resources
    | where type =~ 'microsoft.network/connections'
    | project gwId = tolower(tostring(properties.virtualNetworkGateway1.id))
    | summarize connections = count() by gwId
  ) on gwId
| project name, resourceGroup, gatewayType, sku, connections = coalesce(connections, 0)
| order by connections asc
// VPN and ExpressRoute gateways with their connection count
resources
| where type =~ 'microsoft.network/virtualnetworkgateways'
| project gwId = tolower(id), name, resourceGroup,
    gatewayType = tostring(properties.gatewayType),
    sku = tostring(properties.sku.name)
| join kind=leftouter (
    resources
    | where type =~ 'microsoft.network/connections'
    | project gwId = tolower(tostring(properties.virtualNetworkGateway1.id))
    | summarize connections = count() by gwId
  ) on gwId
| project name, resourceGroup, gatewayType, sku, connections = coalesce(connections, 0)
| order by connections asc

Treat a zero in the second query as a question, not a verdict. A point-to-site VPN gateway serves remote users without any connection resource, so it can show zero connections while half the company depends on it. And leave anything in an AKS node resource group alone: AKS owns those load balancers and public IPs, Microsoft does not support changing them by hand, and in our experience they can look unused from Resource Graph while carrying the cluster's outbound traffic.

For the broader set of idle and orphaned resources, see five queries for orphaned Azure resources.

A one-hour review

If network spend has crept up and nobody can say why, this is a reasonable first pass. Pull last month's cost by service and meter as described above. Name the top three meters and the resource behind each. For each one, write down the workload that sends the traffic. That last step is the hard one, and usually the useful one: once a team sees that their nightly export costs $400 a month in transfer, they tend to change it themselves.

If the network line is only one of several that moved, start with the 30-minute bill diagnosis instead. Idle gateways are among the checks Elos runs every week. Matching traffic to the workload that sent it still needs someone with the architecture diagram open.

Frequently asked questions

Is data transfer between Azure availability zones charged?

No. In May 2024 Microsoft announced that Azure will not charge for data transfer across availability zones, whether resources use private or public IP addresses. Transfer within the same availability zone and between services in the same region is also free. Cross-region transfer, internet egress after the free allowance, and VNet peering remain billed per GB as of October 2026.

How much free egress does Azure include each month?

As of October 2026, the first 100 GB per month of internet egress is free in every Azure region, per Microsoft's bandwidth pricing page. After that, traffic from North America and Europe is billed from $0.087 per GB on the Microsoft network, falling in steps as volume grows. Inbound data transfer into Azure is free at any volume.

Does VNet peering cost money in Azure?

Yes. Peering is charged per GB at both ends: the sending network pays for egress and the receiving network pays for ingress. As of October 2026, list price within one region is $0.01 per GB each way. Global peering between regions costs more and depends on the regions' billing zones. Traffic inside a single virtual network is free.

Does a NAT Gateway add to egress costs?

Yes. A NAT Gateway bills an hourly fee from the moment it is created, plus a per-GB data processing charge that covers outbound traffic and the return traffic. Standard internet egress charges still apply on top. At October 2026 list prices, processing is $0.045 per GB, roughly half the first-tier internet egress rate in North America.

Sources

See where your Azure money is leaking.

Start with the free Score: ten questions, no tenant access. Or go straight to the 20-minute call.