When managing Shared VPCs, most teams allocate dedicated IP subnets for each service project to keep firewall rules simple, but this isolation often leads to poor IP utilization — it is not uncommon to see subnet IP utilization hovering in the low teens. On the other hand, using large shared subnets requires coordinating workload deployments to ensure there is enough internal IP address space for everyone. To optimize these shared networks, you need real-time visibility. The WITH_UTILIZATION query parameter on the Method: subnetworks.list | Compute Engine API solves this by returning the exact count of allocated and free IP addresses for each subnet IP range.
This capability is designed for query-time decisions. For example, if you need to deploy a GCE workload requiring 100 instances, you can search for a subnet with enough capacity. This query-time data comes directly from Google Cloud's internal IP allocator and includes both primary and secondary CIDR ranges.
Automating the search with gcloud and jq
To automate capacity checks before you deploy, you can script this check. The script below uses gcloud compute networks subnets list | Google Cloud SDK to grab the utilization data as JSON, and then uses jq to parse, filter, and sort the subnets based on your required capacity:
#!/bin/bash
# --- Configuration (Replace with your details) ---
PROJECT="<YOUR_PROJECT_ID>"
NETWORK_NAME="<YOUR_VPC_NETWORK_NAME>"
REGION="<YOUR_REGION>"
REQUIRED_IP_CAPACITY=100
echo "Searching $NETWORK_NAME in $REGION for subnets with >= $REQUIRED_IP_CAPACITY free IPs..."
echo "------------------------------------------------------------------------"
# Fetch subnets with utilization data, output as JSON, and pipe to jq
gcloud compute networks subnets list \
--project="$PROJECT" \
--network="$NETWORK_NAME" \
--regions="$REGION" \
--view=WITH_UTILIZATION \
--format=json | \
jq -r --argjson min_ips "$REQUIRED_IP_CAPACITY" '
[
.[] | {
name: .name,
cidr: .ipCidrRange,
# Safely extract totalFreeIp: if it is null, substitute "0" before converting to a number
free_ips: (.utilizationDetails.ipv4Utilizations[0].totalFreeIp // "0" | tonumber)
}
# Keep only the subnets that meet the minimum requirement
| select(.free_ips >= $min_ips)
]
# Sort ascending by free_ips, then reverse to get descending order
| sort_by(.free_ips)
| reverse
# Format the final output into clean, readable strings
| .[]
| "Subnet: \(.name) | CIDR: \(.cidr) | Free IPs: \(.free_ips)"
'
Let's list the configured subnets in our target region first:
~ gcloud compute networks subnets list \
--project="my-gcp-project" \
--format='table(name, region, network, ipCidrRange)'
NAME REGION NETWORK RANGE
subnet-a0 us-south1 vpc-a 10.0.0.0/28
subnet-a1 us-south1 vpc-a 10.0.1.0/28
subnet-a2 us-south1 vpc-a 10.0.2.0/28
subnet-a3 us-south1 vpc-a 10.0.3.0/28
subnet-a4 us-south1 vpc-a 10.0.4.0/28
subnet-a5 us-south1 vpc-a 10.0.5.0/24
subnet-a6 us-south1 vpc-a 10.0.6.0/25
subnet-a7 us-south1 vpc-a
subnet-a8 us-south1 vpc-a
subnet-a9 us-south1 vpc-a
Running the script returns only the subnets that can safely host our 100-instance workload:
~ bash gce-subnet-utilization.sh
Searching vpc-a in us-south1 for subnets with >= 100 free IPv4 addresses...
------------------------------------------------------------------------
Subnet: subnet-a5 | CIDR: 10.0.5.0/24 | Free IPs: 252
Subnet: subnet-a6 | CIDR: 10.0.6.0/25 | Free IPs: 124
Under the hood: Reading the utilization payload
When you request a subnet list with the utilization view, the API returns a utilizationDetails object. For a standard subnet with only a primary IPv4 address configured, the JSON payload looks like this:
"utilizationDetails":{"ipv4Utilizations":[{"totalAllocatedIp":"4","totalFreeIp":"252"}]}
Notice that the totalAllocatedIp is 4. In any primary IPv4 range, Google Cloud reserves four IP addresses for default routing and metadata, as detailed in the Subnets | Virtual Private Cloud - Google Cloud Documentation.
If you have secondary ranges configured (often used for GKE Pods), the API includes utilization metrics for each secondary range, identified by rangeName:
"utilizationDetails":{"ipv4Utilizations":[{"totalAllocatedIp":"4","totalFreeIp":"124"},{"rangeName":"a6-secondary","totalAllocatedIp":"0","totalFreeIp":"4096"}]}
The API also breaks down IPv6 utilization if you are running dual-stack subnets. It tracks external instance IPs, load balancer endpoints, and internal IPv6 allocations separately:
"utilizationDetails":{"externalIpv6InstanceUtilization":{"totalAllocatedIp":{"high":"0","low":"0"},"totalFreeIp":{"high":"0","low":"9223372036854775808"}},"externalIpv6LbUtilization":{"totalAllocatedIp":{"high":"0","low":"0"},"totalFreeIp":{"high":"0","low":"9223372036854775808"}},"internalIpv6Utilization":{"totalAllocatedIp":{"high":"0","low":"8589934592"},"totalFreeIp":{"high":"0","low":"18446744065119617024"}}}
A few quick constraints
- API Support: The
WITH_UTILIZATIONparameter works with both Method: subnetworks.get | Compute Engine and Method: subnetworks.list | Compute Engine. - gcloud Support: You can pass the parameter in
gcloudusing--view=WITH_UTILIZATION, as documented in gcloud compute networks subnets list | Google Cloud SDK. - JSON Strings: The API returns allocated and free counts as strings in the JSON payload. Make sure to cast them (like using
tonumberinjq) before running any mathematical comparisons.
Next steps
Next time you are building a deployment pipeline, try integrating the WITH_UTILIZATION view. It is a simple way to programmatically ensure you have enough network headroom before kicking off a deployment.
For further actions, you may consider blocking this person and/or reporting abuse
