Idle NAT gateways: How to Find Them and What They Cost

Idle NAT gateways are NAT gateways in subnets that no longer run anything, billing an hourly charge for a path nothing uses. They are one of the most reliable sources of pure waste in an AWS account, because nothing in the console flags them and nothing fails when they exist — the bill simply stays higher than it needs to be.

This page covers what they cost, the command that lists yours, and — importantly — what to check before deleting anything.

What they are and why they linger

A NAT gateway charges an hourly rate from creation to deletion, plus a per-gigabyte rate on traffic. The hourly charge continues when the subnets behind it are empty — after a workload was migrated, an environment was decommissioned, or a three-AZ design was scaled down to one.

They are easy to miss because a NAT gateway is not something anyone logs into. It has no console presence beyond a row in a list, and nothing alerts when its traffic drops to zero.

What they cost

At roughly $0.045 per hour, one NAT gateway is about $33 a month before any data charges. A three-AZ VPC runs three, so a decommissioned environment left running costs about $100 a month, $1,200 a year, for a network path carrying nothing.

Multiply by the number of dev, staging, and proof-of-concept VPCs an organisation has accumulated and this is frequently the largest single line of pure waste in an account.

Finding yours

# All NAT gateways with their VPC and subnet.
aws ec2 describe-nat-gateways --filter Name=state,Values=available \
  --query 'NatGateways[].{Id:NatGatewayId,VPC:VpcId,Subnet:SubnetId,Created:CreateTime}' \
  --output table

# How much traffic has each carried in the last week? Near zero means idle.
for id in $(aws ec2 describe-nat-gateways --filter Name=state,Values=available \
              --query 'NatGateways[].NatGatewayId' --output text); do
  bytes=$(aws cloudwatch get-metric-statistics --namespace AWS/NATGateway \
    --metric-name BytesOutToDestination --dimensions Name=NatGatewayId,Value="$id" \
    --start-time "$(date -u -d '7 days ago' +%Y-%m-%dT%H:%M:%SZ)" \
    --end-time "$(date -u +%Y-%m-%dT%H:%M:%SZ)" --period 604800 --statistics Sum \
    --query 'Datapoints[0].Sum' --output text)
  echo "$id: ${bytes:-0} bytes out in 7 days"
done

Anything reporting None or a few kilobytes over a week is a strong candidate.

Before you delete

Check what routes through it before deleting. A NAT gateway with no traffic today may serve a monthly batch job, a quarterly report, or a disaster-recovery path that is idle by design.

aws ec2 describe-route-tables \
  --filters Name=route.nat-gateway-id,Values=nat-0123456789abcdef0 \
  --query 'RouteTables[].{Table:RouteTableId,Subnets:Associations[].SubnetId}'

If the associated subnets contain no running instances, ENIs, or Lambda functions, deletion is safe. If they contain anything at all, find out what it does first.

Deleting a NAT gateway leaves a blackhole route in the route table. Remove the route too, or the next workload placed in that subnet fails in a confusing way.

Stopping them coming back

Tag every NAT gateway with its owning environment, and tie non-production environments to a lifecycle — destroyed nightly, or destroyed when the associated ticket closes. Terraform workspaces that are created and destroyed as a unit avoid this entirely.

Where a NAT gateway is genuinely needed but lightly used, consider whether VPC endpoints alone would serve. A workload that only talks to S3, ECR, and CloudWatch Logs needs no NAT gateway at all.


Prices quoted are us-east-1 list rates, last checked on 2026-08-22, and are shown with the arithmetic so you can substitute the rates for your own region. Always confirm against the AWS pricing page before acting on a number.