When working with AWS VPCs, you'll notice each subnet reserves 5 IP addresses (first four and the last one) from its CIDR block. For a /24 subnet (256 total addresses), this leaves 251 theoretically available addresses. However, in practice, you might encounter discrepancies where fewer IPs appear available than expected.
Several AWS services can consume VPC IP addresses without being obvious:
- RDS instances and Aurora clusters
- Elastic Load Balancers (both ALB and NLB)
- Elasticache nodes
- VPC endpoints (Interface type)
- NAT Gateways
- Transit Gateway attachments
- Site-to-Site VPN connections
Here's a comprehensive bash script to inventory IP usage across all services in a specific subnet:
#!/bin/bash
SUBNET_ID="subnet-1234567890abcdef0"
REGION="us-east-1"
echo "=== IP Address Usage Report for Subnet $SUBNET_ID ==="
echo ""
# EC2 Instances
echo "EC2 Instances:"
aws ec2 describe-instances \
--filters Name=subnet-id,Values=$SUBNET_ID \
--query 'Reservations[*].Instances[*].[PrivateIpAddress,InstanceId,Tags[?Key==Name].Value|[0]]' \
--output table \
--region $REGION
# RDS Instances
echo "\nRDS Instances:"
aws rds describe-db-instances \
--query 'DBInstances[?DBSubnetGroup.Subnets[?SubnetIdentifier=='$SUBNET_ID']].{IP:Endpoint.Address,ID:DBInstanceIdentifier,Type:DBInstanceClass}' \
--output table \
--region $REGION
# ELBs
echo "\nLoad Balancers:"
aws elbv2 describe-load-balancers \
--query 'LoadBalancers[?AvailabilityZones[?SubnetId=='$SUBNET_ID']].{DNS:DNSName,ARN:LoadBalancerArn,Scheme:Scheme}' \
--output table \
--region $REGION
# VPC Endpoints
echo "\nVPC Endpoints:"
aws ec2 describe-vpc-endpoints \
--filters Name=subnet-id,Values=$SUBNET_ID \
--query 'VpcEndpoints[*].[VpcEndpointId,ServiceName,PrivateIpAddress]' \
--output table \
--region $REGION
For more comprehensive tracking, enable VPC Flow Logs and analyze them with Athena:
-- Athena table creation for VPC Flow Logs
CREATE EXTERNAL TABLE vpc_flow_logs (
version int,
account string,
interfaceid string,
sourceaddress string,
destinationaddress string,
sourceport int,
destinationport int,
protocol int,
numpackets int,
numbytes bigint,
starttime bigint,
endtime bigint,
action string,
logstatus string
)
PARTITIONED BY (dt string)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY ' '
LOCATION 's3://your-flow-logs-bucket/AWSLogs/account-id/vpcflowlogs/region/'
TBLPROPERTIES ("skip.header.line.count"="1");
-- Query to find active IPs in a subnet
SELECT DISTINCT sourceaddress
FROM vpc_flow_logs
WHERE date_parse(dt, '%Y/%m/%d') > current_date - interval '7' day
AND sourceaddress LIKE '192.168.1.%' -- Replace with your subnet prefix
ORDER BY sourceaddress;
All VPC resources ultimately use ENIs (Elastic Network Interfaces). This AWS CLI command lists all ENIs in a subnet:
aws ec2 describe-network-interfaces \
--filters Name=subnet-id,Values=$SUBNET_ID \
--query 'NetworkInterfaces[*].[PrivateIpAddress,Description,Status,Attachment.InstanceId]' \
--output table \
--region $REGION
Remember that AWS reserves these IPs in every subnet:
- .0: Network address
- .1: VPC router
- .2: DNS server
- .3: Reserved for future use
- Last IP: Network broadcast (though not used in AWS)
Additionally, AWS might reserve extra IPs for internal services like EKS control planes or Directory Services.
Many AWS users encounter situations where their VPC subnet's available IP count doesn't match expectations. As noted, a /24 subnet theoretically offers 251 usable IPs (256 total minus 5 AWS-reserved addresses), but reality often shows different numbers.
While EC2 instances are the most obvious consumers, several other AWS services quietly claim IP addresses:
- RDS instances (when deployed in VPC)
- Elastic Load Balancers (both ALB and NLB)
- Elastic Network Interfaces (ENIs) from various services
- NAT Gateways
- VPC Endpoints (Interface type)
- ECS Tasks with awsvpc network mode
- Lambda functions in VPC
- ElastiCache clusters
While AWS doesn't provide a single command to show all IP consumers, we can piece together information using these CLI commands:
# First, identify your subnet ID aws ec2 describe-subnets --query 'Subnets[*].[SubnetId,CidrBlock,AvailableIpAddressCount]' --output table # Check EC2 instances in the subnet aws ec2 describe-instances --filters Name=subnet-id,Values=SUBNET_ID \ --query 'Reservations[*].Instances[*].[InstanceId,PrivateIpAddress]' --output table # Check network interfaces aws ec2 describe-network-interfaces --filters Name=subnet-id,Values=SUBNET_ID \ --query 'NetworkInterfaces[*].[Description,PrivateIpAddress]' --output table
For comprehensive tracking, enable AWS Config and create custom rules. This example CloudFormation template sets up IP tracking:
Resources: IPTrackerRule: Type: AWS::Config::ConfigRule Properties: ConfigRuleName: vpc-ip-tracker Scope: ComplianceResourceTypes: - "AWS::EC2::Instance" - "AWS::EC2::NetworkInterface" - "AWS::RDS::DBInstance" Source: Owner: AWS SourceIdentifier: "REQUIRED_TAGS" InputParameters: tag1Key: "VPC" tag2Key: "Subnet"
For large deployments, consider this Python script using Boto3 to inventory IP usage:
import boto3 def get_subnet_ip_inventory(subnet_id): ec2 = boto3.client('ec2') result = { 'ec2_instances': [], 'enis': [], 'other_services': [] } # Get EC2 instances instances = ec2.describe_instances(Filters=[{'Name': 'subnet-id', 'Values': [subnet_id]}]) for reservation in instances['Reservations']: for instance in reservation['Instances']: result['ec2_instances'].append({ 'id': instance['InstanceId'], 'ip': instance['PrivateIpAddress'] }) # Get Network Interfaces interfaces = ec2.describe_network_interfaces(Filters=[{'Name': 'subnet-id', 'Values': [subnet_id]}]) for eni in interfaces['NetworkInterfaces']: if not eni.get('Attachment', {}).get('InstanceId'): result['enis'].append({ 'description': eni['Description'], 'ip': eni['PrivateIpAddress'] }) return result
Proactive measures to avoid running out of IPs:
- Implement proper tagging for all resources
- Use smaller subnets with precise sizing
- Regularly audit unused ENIs and elastic IPs
- Consider using AWS PrivateLink instead of VPC endpoints where possible
- For Lambda functions, evaluate if VPC is truly necessary