ENI and IP Allocation

Overview

VPC CNI manages Elastic Network Interfaces (ENIs) and IP addresses to provide each pod with a native VPC IP. Understanding the allocation mechanics helps optimize pod density and reduce EC2 API throttling.

ENI Basics

Each EC2 instance type has limits:

PropertyDescription
Max ENIsMaximum network interfaces per instance
IPs per ENISecondary IPs per interface (primary + secondaries)
Max PodsCalculated: (ENIs × (IPs_per_ENI - 1)) + 2

Max Pods Formula

Max Pods = (Max ENIs × (IPs per ENI - 1)) + 2

Example for m5.xlarge:
  Max ENIs = 4
  IPs per ENI = 15
  Max Pods = (4 × (15 - 1)) + 2 = 58

The -1 accounts for the primary IP (used by the node itself). The +2 provides a small buffer for the node’s kubelet and kube-proxy.

Default Warm Pool Behavior

By default, ipamd maintains 1 extra ENI as a warm pool to reduce pod launch latency.

Default Allocation Scheme

Pod Count    ENIs Allocated    Warm Pool    Purpose
─────────────────────────────────────────────────────
0-29         1 (primary only)    0         Node baseline
30-58        2                   1         First ENI exhausted
59-87        3                   1         Second ENI exhausted

Why 30?

Each ENI (except the primary) can have up to 30 IPs. When pods exceed the available IPs on existing ENIs, ipamd allocates a new ENI.

Warm Pool Configuration Options

WARM_ENI_TARGET (Default: 1)

# Keep specified number of ENIs warm (with all their IPs)
kubectl set env daemonset/aws-node -n kube-system \
  AWS_VPC_K8S_CNI_WARM_ENI_TARGET=2
SettingBehavior
1 (default)Keep 1 extra ENI with all IPs ready
0No warm pool, allocate only when needed (slower pod launches)
2Keep 2 extra ENIs warm

Trade-off: Higher WARM_ENI_TARGET = faster pod launches but more IP addresses allocated (potential waste).

WARM_IP_TARGET

# Keep specified number of free IP addresses available
kubectl set env daemonset/aws-node -n kube-system \
  AWS_VPC_K8S_CNI_WARM_IP_TARGET=5
SettingBehavior
0 or unsetUse WARM_ENI_TARGET behavior
5Keep 5 free IPs ready at all times

When to use: When you know exactly how many IPs you need available.

Important: Can cause EC2 API throttling in large clusters. Combine with MINIMUM_IP_TARGET.

MINIMUM_IP_TARGET

# Pre-allocate minimum number of IPs regardless of current usage
kubectl set env daemonset/aws-node -n kube-system \
  AWS_VPC_K8S_CNI_MINIMUM_IP_TARGET=30

Purpose: Pre-scale IP allocation for known pod density without wasting IPs during scale-down.

WARM_IP_TARGETMINIMUM_IP_TARGETBehavior
Not set30Pre-allocate 30 IPs, deallocate when pod count drops
330Keep at least 30 IPs, plus 3 free IPs warm

Example: If 30 pods are expected per node, set MINIMUM_IP_TARGET=30 to pre-allocate, with WARM_IP_TARGET=2 for burst.

Combining WARM_IP_TARGET and MINIMUM_IP_TARGET

# Pre-allocate 30 IPs, keep 5 free for scaling
kubectl set env daemonset/aws-node -n kube-system \
  AWS_VPC_K8S_CNI_MINIMUM_IP_TARGET=30 \
  AWS_VPC_K8S_CNI_WARM_IP_TARGET=5
Pod CountIPs AllocatedFree IPsNotes
03030MINIMUM_IP_TARGET ensures 30 allocated
25305WARM_IP_TARGET met
30300All allocated
35350+5 IPs allocated for new pods

WARM_PREFIX_TARGET (Prefix Delegation Mode)

# Keep specified number of /28 prefixes warm
kubectl set env daemonset/aws-node -n kube-system \
  AWS_VPC_K8S_CNI_WARM_PREFIX_TARGET=1

Used only when ENABLE_PREFIX_DELEGATION=true. Each prefix is a /28 (16 IPs).

Allocation Decision Tree

                    Start
                      │
                      ▼
        ┌─────────────────────────┐
        │ ENABLE_PREFIX_DELEGATION│
        └─────────┬───────────────┘
                  │
        ┌─────────┴─────────┐
        │                   │
     true                false
        │                   │
        ▼                   ▼
   WARM_PREFIX_TARGET   WARM_IP_TARGET
        │                   │
        │         ┌─────────┴─────────┐
        │         │                   │
        │      set                 not set
        │         │                   │
        │         ▼                   ▼
        │   WARM_IP_TARGET       WARM_ENI_TARGET
        │         │                   │
        │         └─────────┬─────────┘
        │                   │
        └─────────┬─────────┘
                  │
                  ▼
           EC2 API Call
         (Allocate ENI or IPs)

Practical Configuration Examples

Scenario 1: Standard Workload (Default)

# Use defaults - 1 warm ENI
# Good for: Consistent pod counts, no rapid scaling
env:
- name: AWS_VPC_K8S_CNI_WARM_ENI_TARGET
  value: "1"

Scenario 2: Pre-scaled for Known Density

# Pre-allocate for 30 pods, small warm pool for headroom
env:
- name: AWS_VPC_K8S_CNI_MINIMUM_IP_TARGET
  value: "30"
- name: AWS_VPC_K8S_CNI_WARM_IP_TARGET
  value: "5"
# Good for: Known workload with occasional burst

Scenario 3: High Density with Prefix Delegation

# Use prefix delegation with 1 warm prefix
env:
- name: AWS_VPC_K8S_CNI_ENABLE_PREFIX_DELEGATION
  value: "true"
- name: AWS_VPC_K8S_CNI_WARM_PREFIX_TARGET
  value: "1"
# Good for: Dense clusters, many pods per node

Scenario 4: Cost Optimization (Small Warm Pool)

# Minimize wasted IPs, slower pod launches
env:
- name: AWS_VPC_K8S_CNI_WARM_IP_TARGET
  value: "1"
# Good for: Cost-sensitive, infrequent pod creation

EC2 API Rate Limiting

Impact of Settings

SettingEC2 API CallsThrottle Risk
High WARM_ENI_TARGETFewer (ENI-level)Lower
High WARM_IP_TARGETMore (IP-level)Higher
Low targetsMore frequentHigher

Reducing Throttling

# Use MINIMUM_IP_TARGET instead of WARM_IP_TARGET alone
# Pre-allocates in bulk rather than incremental
 
# Enable subnet discovery (reduces DescribeSubnets calls)
# Enabled by default in v1.18+
 
# Consider MAX_ENI to cap ENIs
kubectl set env daemonset/aws-node -n kube-system \
  AWS_VPC_K8S_CNI_MAX_ENI=5

Monitoring Allocation

Check Current Allocation

# View ENIs and IPs on a node
kubectl exec -n kube-system aws-node-xxxx -- \
  aws ec2 describe-network-interfaces \
  --filters "Name=tag:Name,Values=*-eni-*" \
  --query 'NetworkInterfaces[*].[Attachment.InstanceId,PrivateIpAddress,Status,InterfaceType]'
 
# View ipamd state
kubectl exec -n kube-system aws-node-xxxx -- \
  cat /var/run/aws-node/ipam.json | jq .

Metrics to Watch

MetricDescriptionAlert If
aws_vpc_ipamd_eni_allocatedENIs allocatedAt capacity
aws_vpc_ipamd_prefix_assignedPrefixes assignedLow free
EC2 API throttle %Throttling rate>5%

CloudWatch Insights Query for IP Exhaustion

# Find nodes approaching IP limits
aws logs insights-query \
  --log-group-name /aws/eks/my-cluster/cluster/api \
  --start-time 2024-01-01T00:00:00Z \
  --end-time 2024-01-01T23:59:59Z \
  --query-string 'fields @timestamp, @message | filter @message like "failed to allocate" | limit 20'

Common Allocation Issues

Issue: Pods stuck in Pending despite available IPs

Cause: IP addresses are assigned to pods but not freed properly, or ENI limit reached.

# Check ENI count
kubectl exec -n kube-system aws-node-xxxx -- \
  aws ec2 describe-network-interfaces \
  --filters "Name=attachment.instance-id,Values=$(curl -s http://169.254.169.254/latest/meta-data/instance-id)"
 
# Check ipamd state
kubectl exec -n kube-system aws-node-xxxx -- cat /var/run/aws-node/ipam.json

Solution: Increase instance size or use prefix delegation.

Issue: EC2 API Throttling

Cause: Too many IP allocation calls due to low WARM_IP_TARGET.

# Check CloudWatch for throttle events
aws logs filter-log-events \
  --log-group-name /aws/eks/my-cluster/cluster/api \
  --filter-pattern "ThrottlingException"

Solution: Increase WARM_IP_TARGET or use MINIMUM_IP_TARGET for pre-allocation.

Issue: IP Exhaustion in Subnet

Cause: All IPs in subnet CIDR are allocated.

# Check subnet IP usage
aws ec2 describe-subnets \
  --subnet-ids subnet-xxxxxxxx \
  --query 'Subnets[0].[SubnetId,AvailableIpAddressCount,CidrBlock]'

Solution: Use custom networking to use a different subnet, or expand VPC CIDR.

References