How Your AWS VPC IP Utilization Report Could Save Your Next Deployment

There’s a failure mode that catches even experienced cloud engineers off guard. A deployment pipeline runs flawlessly through staging, clears every gate, and then dies in production with a cryptic error: “There are not enough free addresses in the subnet ‘xyz’ to satisfy your request.” No dramatic alarm. No advance warning. Just a hard stop at the worst possible moment.

The root cause is almost always the same: nobody was tracking IP address utilization inside their AWS VPCs.
This article walks through why IP utilization reporting matters, how AWS counts addresses, how to calculate what you actually have, and what factors you need to account for before the numbers surprise you in production.
Why IP Utilization Reporting Matters
An AWS Virtual Private Cloud (VPC) is a logically isolated network. Every resource you launch inside it — EC2 instances, Lambda functions on VPC, RDS databases, NAT Gateways, load balancers, EKS pods, ECS tasks — consumes one or more private IP addresses from the subnets you have defined.
Unlike a public cloud address space where scale feels infinite, VPC subnets are hard-bounded. A /24 subnet has 256 addresses. A /28 has 16. Once those are gone, nothing new can launch in that subnet until something is released.
The consequences of running out of IPs are severe:
- Auto Scaling Groups silently fail to launch replacement instances during a fault, leaving you under-provisioned when you need capacity most.
- EKS node groups stall mid-rollout, leaving a cluster in a degraded state with half the intended nodes.
- RDS Multi-AZ failovers fail if the standby AZ subnet has no room for the promoted instance.
- CI/CD pipelines break at the deploy stage after passing all prior stages, wasting pipeline minutes and engineer time.
- Incident response is slower because engineers diagnosing a production failure now also have to diagnose a networking constraint they didn’t know existed.
None of these failures announce themselves in advance. They happen the moment a resource tries to acquire an address that isn’t there. A proactive IP utilization report turns an invisible constraint into a visible metric you can act on before it becomes an incident.
On the flip side, over-allocation of IP space is a common phenomenon as well. Before you know it, the whole /15 (131,072) or /16 (65,536) IP allocation is exhausted. As a part of regular housekeeping, it is important to clean up unused VPCs and rightsize the over-allocated ones. You may be surprised to discover how application owners can be overly cautious to request excessive margin in IP space. In some cases, they may be unaware of the application architecture and required IP count to support it. A proper gatekeeping can keep help design right-sized VPCs. You can’t simply shrink a VPC’s IP allocation to reclaim the unused.
How AWS Counts IP Addresses
Before you can calculate utilization, you need to understand AWS’s reservation rules.
The Five Reserved Addresses Per Subnet
AWS automatically reserves five IP addresses in every subnet, regardless of its size. For a subnet with CIDR 10.0.1.0/24:
| Address | Reserved For |
| 10.0.1.0 | Network address |
| 10.0.1.1 | VPC router |
| 10.0.1.2 | DNS server (VPC base + 2) |
| 10.0.1.3 | Future AWS use |
| 10.0.1.255 | Broadcast (not used, but reserved) |
This means a /24 gives you 251 usable addresses, not 256. A /28 gives you 11, not 16. This distinction matters most at small prefix sizes — a /28 loses nearly a third of its address space to reservations.
Consumable IPs by Prefix Length
| CIDR Prefix | Total Addresses | AWS Reserved | Usable (Provisioned) |
| /16 | 65,536 | 5 | 65,531 |
| /20 | 4,096 | 5 | 4,091 |
| /21 | 2048 | 5 | 2043 |
| /22 | 1024 | 5 | 1019 |
| /23 | 512 | 5 | 507 |
| /24 | 256 | 5 | 251 |
| /25 | 128 | 5 | 123 |
| /26 | 64 | 5 | 59 |
| /27 | 32 | 5 | 27 |
| /28 | 16 | 5 | 11 |
The Three Numbers That Matter
Every IP utilization report should track three values per subnet and roll them up per VPC:
Provisioned IPs = 2^(32 – prefix_length) – 5
Available IPs = Reported by AWS in real time (AvailableIpAddressCount)
Used IPs = Provisioned – Available
And the derived metric:
Utilization % = (Used / Provisioned) × 100
How to Read AvailableIpAddressCount
AWS exposes AvailableIpAddressCount on every subnet through the EC2 API. This is a live count — it decreases the moment a network interface is assigned an address and increases the moment one is released. It is the most accurate signal you have for real-time availability.
Bash Shell:
–filters “Name=vpc-id,Values=vpc-0e123abc456defxyz” \
–query ‘Subnets[*].{ID:SubnetId,CIDR:CidrBlock,AvailableIps:AvailableIpAddressCount, Name:Tags[?Key==`Name`]| [0].Value}’
Combining that live number with the calculated provisioned count gives you utilization without any guesswork.
What Consumes IPs (More Than You Think)
The first thing most people think of is EC2 instances. But IP consumption in a modern AWS environment is far broader.
1) Primary Network Interfaces
Every EC2 instance gets one primary ENI with one primary private IP. That’s the baseline. But many workloads attach additional interfaces or additional IPs per interface.
2) Secondary and Alias IPs
EC2 instances support multiple private IPs per ENI. Some applications bind to multiple addresses. Elastic IPs still consume a private IP from the subnet in addition to the public address.
3) EKS and Container Networking
This is the single biggest surprise for teams moving to Kubernetes on AWS. The AWS VPC CNI plugin (the default EKS networking mode) assigns a real VPC IP address to every pod, not just every node. A single /24 subnet with 251 usable addresses can fill up with pods alone — especially with smaller instance types that have lower ENI limits, since the CNI pre-warms ENIs by allocating IPs speculatively.
For EKS clusters, plan your subnets an order of magnitude larger than you think you need, or use prefix delegation(assigning /28 blocks to nodes rather than individual IPs) to extend subnet capacity significantly.
4) RDS and ElastiCache
Each RDS instance consumes at least one IP. Multi-AZ deployments consume one per AZ. Read replicas consume additional IPs. ElastiCache clusters consume one per node.
5) Load Balancers
Application Load Balancers and Network Load Balancers consume IPs from the subnets they are placed in — one per AZ, per load balancer. A highly available ALB spanning three AZs consumes three IPs from your subnet allocation.
6) NAT Gateways
Each NAT Gateway consumes one IP in its subnet.
7) VPC Endpoints and PrivateLink
Interface VPC endpoints (for services like S3, ECR, Secrets Manager) consume one IP per AZ they’re deployed into. A single endpoint across three AZs consumes three IPs.
8) Lambda on VPC
Lambda functions attached to a VPC consume IPs from the subnet. At high concurrency, this can be significant — though AWS has introduced Hyperplane ENIs to reduce the per-function IP cost, the consumption is not zero.
9) Transit Gateway Attachments
Each Transit Gateway attachment to a VPC consumes one IP per subnet used for the attachment.
10) Cloud WAN Attachments
Each Cloud WAN attachment to a VPC consumes one IP per subnet used for the attachment.
Factors to Consider in Your Utilization Report
Raw numbers tell you where you are. Context tells you whether to act.
1) Growth Trajectory
A subnet at 60% utilization is fine today. If your EKS cluster is growing by 15% a month, you may have three months before it becomes a problem. Your report should include a trend over time, not just a point-in-time snapshot.
2) Subnet Role and Traffic Pattern
Transit subnets, management subnets, and data plane subnets have very different consumption profiles. A /28 is appropriate for a management subnet hosting a handful of bastion hosts. It is catastrophically undersized for a pod subnet.
3) AZ Symmetry
AWS does not balance resources across Availability Zones automatically in all cases. If Auto Scaling prefers one AZ due to spot pricing or capacity, that AZ’s subnets can become exhausted while the others remain largely empty. Report utilization per subnet, not just per VPC.
3) Reserved Headroom for Failover
If a subnet is at 80% utilization under normal load, a failover event that doubles the instance count in that subnet will immediately exhaust it. Design for peak failover load, not steady-state load.
IPAM and Secondary CIDRs
AWS VPC IPAM (IP Address Manager) allows you to add secondary CIDR blocks to a VPC when the primary space is running low. If you’re approaching exhaustion, a secondary CIDR block buys time — but it adds operational complexity and should be planned, not scrambled for during an incident.
Thresholds Worth Acting On
As a practical starting point for alert thresholds:
|
Utilization |
Recommended Action |
|
< 60% |
Healthy — monitor on a regular schedule |
|
60–75% |
Review growth trend; plan subnet expansion timeline |
|
75–85% |
Plan and schedule expansion; raise with infrastructure team |
|
85–90% |
Immediate planning required; restrict non-critical launches |
|
> 90% |
Critical — add capacity or restrict new deployments now |
These thresholds should be tighter for subnets serving Auto Scaling workloads or EKS pods, where consumption can spike rapidly and without warning.
Building the Report
A minimal IP utilization report needs to answer five questions for every subnet:
- What is the subnet’s CIDR and therefore its provisioned IP count?
- How many IPs are currently available?
- How many are in use, and what is the utilization percentage?
- What workload type does this subnet serve?
- Is utilization trending up, stable, or down?
The AWS CLI and SDKs give you everything needed to answer the first three. Tagging strategy answers the fourth — subnets tagged with their role (public, private, data, transit, management) make it trivial to segment your report by workload type. CloudWatch metrics and scheduled Lambda functions can answer the fifth by storing historical snapshots.
At the VPC level, roll up the per-subnet numbers to give a total provisioned, used, and available count across all subnets in that VPC. This gives leadership and platform teams a single number per VPC without losing the subnet-level detail needed for operational decisions.
Assumption Made
- Consumable IPs per subset = Provisioned IPs (per subnet IP prefix) – 5 (IP overhead per subnet)
- Free IP as AWS subnet metric (AvailableIpAddressCount)
- Transit subnet excluded from consumable IPs as it is strictly used for Transit Gateway or Cloud WAN attachment in my use case.
- Transit Subnet Size: /28
- Transit Subnet Name: <Corp>-<Env>-<WorkLoad>-<Category>-<Region>-transit-<AZ>
- Example-1: agill-dev-asset-reporting-app-use1-transit-a
- Example-2: agill-prod-log-aggregation-infra-usw2-transit-c
- Firewall subnet is excluded from consumable IPs as it is strictly used for Palo Alto Cloud NGFW Endpoint deployment in my test case.
- Firewall Subnet Size: /28
- Firewall Subnet Name: <Corp>-<Env>-<WorkLoad>-<Category>-<Region>transit-<AZ>
- Example: agill-qa-inspection-infra-euc2-firewall-c
Please remember to update the script to reflect your use case and subnet naming standards. Transit and Firewall subnets are off-limits for Application workloads in my scenario. Based on my assumptions, I filtered out these subnets as in snippet below. Feel free to enhance the script by querying the VPC attachment and subnets specified for it. Same way, firewall deployment can be easily verified by additional CLI commands API calls
if [[ "$SUBNET_NAME" =~ -transit-[abcd]?$ && "${SUBNET_CIDR: -2}" == "28" ]] ; then
#echo "transit subnet found, skipping addition to total consumable and total available"
SUBNET_SKIPPED=true
elif [[ "$SUBNET_NAME" =~ -firewall-[abcd]?$ && "${SUBNET_CIDR: -2}" == "28" ]] ; then
#echo "firewall subnet found, skipping addition to total consumable and total available"
SUBNET_SKIPPED=true
How to Run Report
You can run it in a variety of ways.
- Download the script.
- Run the provided script “as is” in AWS Cloud Shell. You either same the script into a file or simply copy paste into shell prompt.
- ./vpc-report.sh > vpc-report.csv
- copay and past the script into shel prompt.
- Run from a Linux EC2. AWS CLI, jq and EC2 Role with proper ReadOnly permissions will be required. Amazon Linux 2023 comes with preinstalled AWS CLI. g
- Embed the script into a Lambda with “Amazon Linux 2023” runtime.
- Port the logic into Python and use with it with Lambda runtime.
- Distribute the output file via S3 bucket, an email distribution list and any other form of fire share.
- CSV file can be opened into MS Excel or Mac Numbers for easy viewing.
- The last column “percentage free” can be easily converted into “percentage utilized:
-
if [ "$TOTAL_CONSUMABLE" -ne 0 ]; then PERCENT_FREE=$((TOTAL_AVAILABLE * 100 / TOTAL_CONSUMABLE)) #what's in the script now PERCENT_UTILIZED=$(((TOTAL_CONSUMABLE-TOTAL_AVAILABLE) * 100 / TOTAL_CONSUMABLE)) #can be updated to this
-
It will generate comma separated values like following.
vpc-0a123abd456xyz789,agill-dev-integration-euc2-vpc,10.10.255.64/27,11,1,9% vpc-0b123abd456xyz789,agill-dev-security-euc2-vpc,10.10.192.0/24,162,161,99% vpc-0c123abd456xyz789,agill-dev-reporting-euc2-vpc,10.10.57.0/24,162,140,86% vpc-0d123abd456xyz789,agill-dev-finance-euc2-vpc,10.10.226.0/23,408,313,76% vpc-0e123abd456xyz789,agill-dev-marketing-euc2-vpc,10.10.219.0/24,66,66,100% vpc-0f123abd456xyz789,agill-dev-sales-euc2-vpc,10.10.255.0/27,11,0,0%
Download Script File
The Takeaway
IP address space inside an AWS VPC is a finite resource with no built-in guardrails. AWS will not warn you when you’re running low. It will simply refuse to launch the next resource that needs an address.
The teams that avoid IP exhaustion incidents aren’t the ones with the biggest CIDR blocks — they’re the ones that treat IP utilization as a first-class operational metric, report on it regularly, and act on it before the threshold becomes a crisis. A well-built utilization report costs a few hours to build and saves potentially hours of incident response, failed deployments, and emergency re-architecture.
Start with the numbers. Know your subnets. Know your headroom.
References
Subnet CIDR blocks and Reserved IPs
AWS command line tools & Amazon Linux 2
Installing, updating, and uninstalling the AWS CLI
Installing JQ on Amazon Linux 2
Tagged: AWS, VPC, Networking, Cloud Infrastructure, DevOps, Site Reliability, IP Management