Symptoms
- TCP retransmits, latency spikes or random connection failures on a busy instance (proxy, NAT instance, load generator, database).
- DNS lookups occasionally time out on that instance only.
- CloudWatch
NetworkIn/NetworkOutare below the instance's advertised bandwidth.
Every EC2 instance has allowances beyond headline bandwidth: packets per second, number of tracked connections, and link-local traffic (DNS, IMDS, NTP). When one is exceeded, packets are queued or dropped silently. The ENA driver counts each case.
Step 1: read the counters
Linux:
ethtool -S eth0 | grep -E "allowance_exceeded|conntrack_allowance_available"
bw_in_allowance_exceeded: 0
bw_out_allowance_exceeded: 18234
pps_allowance_exceeded: 0
conntrack_allowance_exceeded: 4211
linklocal_allowance_exceeded: 960
conntrack_allowance_available: 120
Windows (PowerShell, ENA driver 2.2+ exposes them as adapter statistics; see the ENA Windows driver docs for your version):
Get-NetAdapterStatistics -Name "Ethernet" | Format-List *
The counters are cumulative since driver load. Take two readings a minute apart; what matters is whether they increase during the problem.
| Counter | Limit exceeded |
|---|---|
bw_in_allowance_exceeded / bw_out_allowance_exceeded | Aggregate bandwidth (including bursts above the baseline for burstable network sizes) |
pps_allowance_exceeded | Packets per second; small packets hit this before bandwidth |
conntrack_allowance_exceeded | Number of tracked connections for the instance size |
linklocal_allowance_exceeded | PPS to link-local services: Route 53 Resolver, IMDS, Amazon Time Sync |
conntrack_allowance_available | Remaining tracked-connection capacity (newer drivers) |
Step 2: collect them in CloudWatch
CloudWatch agent config (Linux):
{
"metrics": {
"append_dimensions": { "InstanceId": "${aws:InstanceId}" },
"metrics_collected": {
"ethtool": {
"interface_include": ["eth0"],
"metrics_include": [
"bw_in_allowance_exceeded", "bw_out_allowance_exceeded",
"pps_allowance_exceeded", "conntrack_allowance_exceeded",
"linklocal_allowance_exceeded", "conntrack_allowance_available"
]
}
}
}
}
Alarm on the rate of change (a metric math RATE() or the difference between periods), not the absolute value.
Step 3: fix by counter
Bandwidth (bw_*)
- Instances "up to X Gbps" have a baseline and burst using credits. Sustained traffic above baseline gets shaped. Check the baseline for your type in the EC2 network performance docs.
- Fix: larger size, a network-optimised family (the
nvariants), or spread across instances. - Single-flow limits apply too (a single TCP flow is capped lower than aggregate outside a cluster placement group); use parallel streams for bulk transfers.
Packets per second
- Typical for DNS servers, VoIP, game servers, small-message proxies.
- Fix: larger instance, enable multiple queues (default with ENA), larger messages/batching, or ENA Express where supported.
Connection tracking
Security groups track connections so replies are allowed back. Each instance size can track a limited number.
- Connections are untracked (and don't count) when the security group rules allow the traffic in both directions without restriction, for example inbound
0.0.0.0/0on the port and outbound0.0.0.0/0all traffic. Some traffic is always tracked (ICMP, and flows matched by restrictive rules). - Many idle or half-closed connections fill the table. Reduce idle timeouts per ENI:
aws ec2 modify-network-interface-attribute --network-interface-id eni-0abc \
--connection-tracking-specification TcpEstablishedTimeout=600,UdpStreamTimeout=60,UdpTimeout=30
- On the OS: close idle connections in the app, enable keepalive, and tune connection pools.
- For high-connection front ends (proxies, NAT instances), larger instances or a load balancer in front.
Warning
Changing security group rules to "allow all" to make connections untracked is a security decision, not just a performance tweak. Only do it where an upstream control (NLB, firewall, NACLs) restricts traffic.
Link-local (DNS, IMDS, NTP)
The Amazon-provided DNS resolver, IMDS and Time Sync share a per-ENI packet rate limit (documented as 1024 packets per second per ENI for these services). Busy proxies or crawlers that resolve a name per request exceed it.
- Run a local caching resolver (
systemd-resolved,dnsmasq,unbound) and point the OS at it. - Cache IMDS credentials (SDKs already do; custom scripts often don't).
- Reuse connections in the application.
Step 4: confirm the fix
Re-run ethtool -S eth0 | grep allowance during peak load. The counter you fixed should stop increasing. Watch application latency and TCP retransmits (nstat -az TcpRetransSegs).
Prevent it next time
- Collect the ENA allowance counters on every production instance by default.
- Load test with realistic connection counts and packet sizes, not just throughput.
- Size network-heavy roles (proxies, NAT, DNS) by PPS and connections, not CPU.
- Use local DNS caching on every instance that makes many outbound calls.