How to Find and Delete Idle NAT Gateways on AWS

A NAT Gateway bills hourly and per-GB whether or not anything is actually routing through it. Here's how to find the idle ones yourself with the AWS CLI, and exactly what to check before deleting one.

A NAT Gateway costs money in two ways: an hourly charge for the resource existing at all (currently around $0.045/hour in us-east-1, roughly $32-33/month), and a per-GB charge for data processed through it. The hourly charge is the trap — it bills exactly the same whether the gateway is routing gigabytes of traffic or absolutely nothing, which happens more often than you'd think: a staging VPC left over from a project that wrapped up, a private subnet whose instances were terminated but whose route table was never cleaned up, a multi-AZ setup where one AZ's NAT Gateway quietly stopped being used after a failover.

Step 1 — list your NAT Gateways

aws ec2 describe-nat-gateways \
  --filter "Name=state,Values=available" \
  --query "NatGateways[].{Id:NatGatewayId,VpcId:VpcId,SubnetId:SubnetId}" \
  --output table

Run this in every region you actually use, not just your default one — a forgotten NAT Gateway in a region you stopped using is exactly the kind of thing that survives for years unnoticed.

Step 2 — check whether it's actually moving traffic

CloudWatch publishes BytesOutToDestination for every NAT Gateway automatically, even at zero — so a genuinely idle one is unambiguous, not an absence of data:

aws cloudwatch get-metric-statistics \
  --namespace AWS/NATGateway \
  --metric-name BytesOutToDestination \
  --dimensions Name=NatGatewayId,Value=nat-0a1b2c3d4e5f \
  --start-time $(date -u -d '14 days ago' +%Y-%m-%dT%H:%M:%S) \
  --end-time $(date -u +%Y-%m-%dT%H:%M:%S) \
  --period 86400 \
  --statistics Sum

If every daily sum comes back as 0.0 over two full weeks, you're looking at a genuinely idle gateway, not just a quiet day. Fourteen days is usually enough to rule out a weekly batch job, but if anything in that VPC runs monthly (an end-of-month report, a quarterly job), check further back before deciding.

Step 3 — confirm nothing still depends on it

Zero traffic today doesn't mean nothing points at it. Check every route table in the VPC for a route through this NAT Gateway, and look at what subnet that route table is associated with:

aws ec2 describe-route-tables \
  --filters "Name=route.nat-gateway-id,Values=nat-0a1b2c3d4e5f" \
  --query "RouteTables[].{RouteTableId:RouteTableId,Associations:Associations[].SubnetId}"

If a subnet is still associated with that route table, confirm nothing in it is scheduled to launch later (an Auto Scaling group scaled to zero, a Lambda in that subnet that only runs on a schedule) before you delete anything.

Step 4 — delete it

aws ec2 delete-nat-gateway --nat-gateway-id nat-0a1b2c3d4e5f --region us-east-1

Deletion takes a few minutes. The Elastic IP that was attached to the gateway is released back to you automatically but stays allocated to your account — an unassociated Elastic IP bills its own small hourly charge, so release it separately once you've confirmed the gateway is gone:

aws ec2 describe-addresses --query "Addresses[?AssociationId==null]"
aws ec2 release-address --allocation-id eipalloc-xxxxxxxx

The honest caveat

Fourteen days of zero bytes is a strong signal, not a guarantee. Route tables can be shared across environments in ways that aren't obvious from the NAT Gateway alone, and a disaster-recovery subnet is supposed to look idle right up until the day it isn't. Check the route tables in step 3 before you delete anything — the five minutes it takes is cheaper than an incident.

CostLens runs this exact check automatically, every scan.

Connect your AWS account read-only and it surfaces every idle NAT Gateway (plus the rest of what this post doesn't cover) with the dollar figure and the exact command already filled in.

Connect your AWS account