Skip to main content
emnode
Cost

Remove load balancers with empty backend pools

A Standard Load Balancer with no backend targets routes nothing, but it still carries the rule-based hourly charge and any data processed. This lesson finds those idle balancers across a subscription and removes them safely.

12 min·10 sections·AZURE

Last reviewed

Idle load balancers: the basics

What does a load balancer with an empty backend pool actually cost?

A load balancer exists to spread traffic across a backend pool of virtual machines, scale set instances or IP addresses. When that pool is empty, the balancer has nowhere to send anything: it is a frontend with no destination. The resource still exists, still holds its frontend IP configuration and its load-balancing rules, and on the Standard tier it still bills. This is one of the most common shapes of Azure waste because the balancer rarely starts empty. It is left empty when the workload behind it is decommissioned, when a scale set is deleted but its balancer is not, or when a deployment is torn down and the networking is left behind.

The Standard Load Balancer is billed on a rule-based model: a set number of load-balancing and outbound rules are included in an hourly charge, additional rules are charged per rule per hour, and all data processed through the balancer is billed per gigabyte. A balancer with an empty backend pool processes no data, so the data charge is usually zero, but the rules keep accruing their hourly charge for as long as the resource exists. The Basic Load Balancer is free, so this is specifically a Standard-tier problem, and Standard is now the default and the only tier Microsoft is carrying forward.

The work is to find every Standard Load Balancer whose backend pools have no targets, confirm that the absence of targets is permanent rather than a scale set that has scaled to zero, and delete the ones that are genuinely orphaned along with the public IP and any other networking they were dragging along. Done across an estate this is pure waste removal: nothing is serving traffic through these resources, so removing them changes the bill without changing behaviour.

In this lesson you will learn why a Standard Load Balancer with an empty backend pool still bills, how the Cost Optimization workbook surfaces these as idle resources on its Networking tab, how to find every one of them across a subscription with a single query, and how to delete the orphaned ones safely without taking down a scale set that has merely scaled to zero. The Keep learning section links the exact Microsoft pricing and CLI references behind every step.

Fun fact

The balancer that outlived its workload

When Microsoft retired the Basic Load Balancer and made Standard the default, a quiet billing change came with it. The Basic tier was free, so an empty Basic balancer cost nothing and nobody cared if one was left behind. The Standard tier bills its rules by the hour whether or not anything sits in the backend pool, so the exact same 'leftover after a workload is gone' pattern that was free for years now silently accrues a charge. The resource did not change behaviour. The price of forgetting about it did.

Finding idle load balancers across a subscription

Priya runs the platform team at a company that deploys most of its infrastructure through pipelines. The monthly Cost Optimization workbook flags a cluster of Standard Load Balancers with empty backend pools on its Networking tab, several of them in resource groups whose workloads were retired months ago.

Rather than open each balancer in the portal, Priya lists every Standard Load Balancer in the subscription and counts the targets in its backend pools, so the genuinely empty ones can be separated from those still wired to a scale set before anything is deleted.

List every Standard Load Balancer and the number of backend addresses across its pools. A balancer summing to zero has no targets to route to.

$ az network lb list --query "[?sku.name=='Standard'].{name:name, rg:resourceGroup, targets:sum(backendAddressPools[].length(loadBalancerBackendAddresses))}" -o table
Name Rg Targets
---------------- ------------------ ---------
lb-legacy-api rg-retired-api 0
lb-web-prod rg-web 4
lb-batch-vmss rg-batch 0
# lb-legacy-api: pool empty, resource group retired. Orphan, safe to remove.
# lb-batch-vmss: empty now, but check the scale set has not just scaled to zero.

An empty pool in a retired resource group is a clear orphan. An empty pool fronting a scale set may simply be scaled to zero and will refill, so confirm before deleting.

How the workbook decides a load balancer is idledeep dive

The Cost Optimization workbook runs an Azure Resource Graph query against load balancer resources and inspects their backend address pools. A pool can hold targets in two ways: 'loadBalancerBackendAddresses', which are IP-based members, and 'backendIPConfigurations', which are the NIC-based members attached when virtual machines or scale set instances join the pool. The workbook expands each pool and counts the targets across both shapes. When every pool on a balancer counts zero, the balancer has no data path and is flagged as idle on the Networking tab.

The reason an idle balancer still bills comes down to the Standard tier's pricing model. The charge is driven by configured load-balancing and outbound rules, billed per hour, plus data processed per gigabyte. An empty pool processes no data, so the data component is effectively zero, but the rules remain configured on the resource and keep accruing their hourly charge regardless of whether any backend exists. The Basic tier is free, so an idle Basic balancer costs nothing; this is purely a Standard-tier concern, and Standard is the default and the tier Microsoft is carrying forward.

One nuance is worth respecting before you delete. A balancer fronting a virtual machine scale set with autoscaling can show an empty pool legitimately when the fleet has scaled to zero instances. The pool is not orphaned, it is waiting to refill on the next scale-out. The workbook flags it the same as a true orphan because at the moment of the query it has no targets, so the human step is to confirm whether the emptiness is permanent or transient before removing the resource.

What is the impact of leaving idle balancers running?

The direct impact is a recurring charge for nothing. Each idle Standard Load Balancer bills its configured rules every hour for as long as it exists, with no offsetting value because no traffic flows through it. The per-resource amount is small, which is exactly why it is easy to ignore, but it never stops and it accumulates across every orphaned balancer in the estate.

The second-order impact is what the idle balancer drags along with it. An orphaned balancer usually still holds a Standard public IP address, which carries its own hourly charge, and may sit in a resource group full of other leftover networking from the retired workload. The balancer is often the visible flag for a cluster of orphaned resources, so finding it is a prompt to clean up the public IP, the empty resource group and anything else the decommission left behind.

The third impact is a hygiene signal. A growing count of idle balancers on the Cost Optimization workbook means networking is outliving the workloads it was built for, which points to a teardown process that removes compute but not the networking around it. The cost of any single balancer is trivial; the value of noticing the pattern is that it surfaces a gap in how resources are decommissioned across the whole estate.

How do you remove idle balancers safely?

Work the cleanup as a short loop: find every empty balancer, confirm the emptiness is permanent, then delete the orphans together with the networking they dragged along. The order matters, because the only way to cause harm here is to delete a scale set's balancer while it is briefly scaled to zero.

1. Inventory every Standard balancer by target count

List every Standard Load Balancer in the subscription and count the targets across its backend pools, summing both the IP-based members and the NIC-based members. Balancers summing to zero are the candidates. Use this inventory as the source of truth rather than the workbook count, because the workbook is a point-in-time snapshot.

2. Confirm the empty pool is permanent

For each candidate, decide whether the empty pool is an orphan or a transient state. A balancer in a retired resource group is an orphan. A balancer fronting an autoscaling scale set may simply be scaled to zero and will refill, so check the scale set's instance count and autoscale settings before touching it. This is the one step that prevents an outage.

3. Delete the orphans and what they dragged along

Delete each confirmed orphan, then remove the Standard public IP it was fronting, which carries its own charge, and clean up any empty resource group left behind by the retired workload. The balancer is usually the visible flag for a wider cluster of orphaned networking, so treat its removal as the start of the cleanup, not the end.

4. Tie networking lifecycle to workload lifecycle

Stop the orphans coming back by making the load balancer and its public IP part of the same teardown as the workload. When networking is provisioned and destroyed in the same infrastructure-as-code template as the compute it serves, a retired workload takes its balancer with it, and the idle-balancer count on the workbook stays near zero by design.

# 1. Find every Standard Load Balancer whose backend pools have no targets.
az network lb list \
  --query "[?sku.name=='Standard' && sum(backendAddressPools[].length(loadBalancerBackendAddresses))==\`0\` && sum(backendAddressPools[].length(backendIPConfigurations))==\`0\`].{name:name, rg:resourceGroup}" \
  -o table

# 2. For each candidate fronting a scale set, confirm it is not just scaled to zero
#    before deleting (skip this for balancers in retired resource groups).
az vmss list --resource-group "<resource-group>" \
  --query "[].{name:name, capacity:sku.capacity}" -o table

# 3. Delete a confirmed orphan, then the public IP it was fronting.
az network lb delete --resource-group "<resource-group>" --name "<lb-name>"
az network public-ip delete --resource-group "<resource-group>" --name "<public-ip-name>"

Quick quiz

Question 1 of 5

Why does a Standard Load Balancer with an empty backend pool still appear on the bill?

You can now treat idle load balancers as one quick loop rather than a scatter of small findings: inventory every Standard balancer by target count, confirm an empty pool is permanent rather than a scale set scaled to zero, delete the orphans together with the public IPs and resource groups they dragged along, and tie networking lifecycle to workload lifecycle so they do not come back. The Keep learning links above cover the pricing and commands behind each step.

Back to the library