Home · About · Services · Industries · Projects · Products · Process · Blog · FAQ · Contact
AWS hidden costs: NAT gateways, data transfer and idle resources Where AWS bills quietly grow: NAT gateway processing, egress and cross-zone transfer, public IPv4 charges and forgotten resources, with commands to find each…
A team sends us their AWS bill and asks why it has doubled. The EC2 instances have not changed. The database is the same size. When we group the bill by usage type, the growth is in lines nobody chose on purpose: NAT gateway data processing, cross-zone transfer, public IPv4 addresses and a few hundred gigabytes of logs kept forever.
The short answer: hidden AWS costs are usually not compute. They are NAT gateway hourly and per-GB charges, data transfer out to the internet and between Availability Zones, public IPv4 addresses, and resources that keep billing after their purpose has gone, such as unattached EBS volumes, old snapshots, idle load balancers and log groups with no retention.
Prices below are on-demand rates for us-east-1 as published on the AWS pricing pages. Other regions, including Mumbai, are often higher, so check the page for your region before you model anything.
Find the hidden lines in your own bill first Before changing anything, look at the bill the way AWS meters it. In Cost Explorer, set the date range to the last three months, group by Usage type, and filter out the obvious compute lines. Usage type names carry a region prefix and tell you exactly what is being charged. These are the ones we look for:
NatGateway-Hours and NatGateway-Bytes: the gateway existing, and every gigabyte it processes. DataTransfer-Out-Bytes: traffic from AWS to the internet. DataTransfer-Regional-Bytes: traffic between Availability Zones in the same region. PublicIPv4:InUseAddress and PublicIPv4:IdleAddress: every public IPv4 address, attached or not. EBS:VolumeUsage (suffixed by volume type) and EBS:SnapshotUsage: disks and snapshots, whether or not anything uses them. TimedStorage-ByteHrs under CloudWatch, and DataProcessing-Bytes for log ingestion. Turn on cost allocation tags for environment and owner at the same time. Tagged costs appear from the day you activate a tag. You can request a backfill of up to 12 months, but only for resources that already carried the tag, so start tagging early.
NAT gateways: an hourly charge plus a toll on every gigabyte A managed NAT gateway costs $0.045 per hour and $0.045 per GB of data it processes. The hourly part is about $33 a month per gateway, and the recommended pattern is one gateway per Availability Zone, so a three-zone VPC starts at roughly $99 a month before any traffic. The per-GB part is the one that grows quietly, because it applies to everything private subnets send out through the gateway, including traffic to other AWS services.
The common culprits are container image pulls from ECR or Docker Hub, application uploads and downloads to S3, backups shipped to S3, and agents sending metrics and logs. A nightly backup of 50 GB through a NAT gateway adds about $67 a month in processing charges alone, for traffic that never needed to leave AWS's network.
The fixes, in the order we usually apply them:
Add gateway VPC endpoints for S3 and DynamoDB. They are free, and traffic to those services stops passing through the NAT gateway once the route tables are updated. Add interface endpoints (PrivateLink) for heavy users such as ECR, CloudWatch Logs or Secrets Manager. They cost $0.01 per hour per zone plus $0.01 per GB, so they pay off only where traffic is high; compare before adding many. For development and staging VPCs, one NAT gateway shared across zones is often acceptable. You lose zone redundancy for outbound traffic, which matters in production and rarely in a test environment. Ask whether the workload needs private subnets at all. A small single-instance service in a public subnet with a tight security group may need no NAT gateway. # Free gateway endpoint for S3 (gateway is the default endpoint type)
aws ec2 create-vpc-endpoint \
--vpc-id vpc-0abc1234 \
--service-name com.amazonaws.ap-south-1.s3 \
--route-table-ids rtb-0priv1 rtb-0priv2
# Confirm which endpoints exist in the VPC
aws ec2 describe-vpc-endpoints \
--filters Name=vpc-id,Values=vpc-0abc1234 \
--query 'VpcEndpoints[].[ServiceName,VpcEndpointType,State]' --output tableIf you cannot tell where NAT traffic is going, enable VPC Flow Logs on the NAT gateway's network interface for a day and group by destination. The answer is usually one or two services.
Data transfer: egress and cross-zone traffic Inbound traffic is free. Outbound traffic to the internet has 100 GB a month free across your account, then costs $0.09 per GB for the first 10 TB in us-east-1. For a site serving large downloads, images or video directly from EC2 or S3, that is often the single biggest line after compute.
Traffic between Availability Zones in the same region is less obvious. It costs $0.01 per GB in each direction, so a gigabyte crossing zones is billed twice. Chatty services spread across zones, a database replica in another zone, or a Network Load Balancer with cross-zone load balancing turned on can all generate it. Application Load Balancer traffic to targets in other zones is not charged. A replicated cache or message queue that moves a few terabytes a month between zones becomes a real cost.
Put CloudFront in front of public files and downloads. Transfer from S3 or EC2 origins to CloudFront is free, and CloudFront's always-free tier includes 1 TB of transfer out a month. Compress responses and set proper cache headers so repeat visitors do not download the same assets. Keep services that talk constantly in the same zone where the availability trade-off is acceptable, and keep multi-zone placement for the parts that genuinely need it. Check whether backups or replicas are going to another region. Inter-region transfer is charged per GB, and it should be a deliberate choice, not a default. Public IPv4 addresses Since February 2024, AWS charges $0.005 per hour for every public IPv4 address, whether it is attached to a running instance or sitting idle. That is about $3.60 a month each. It sounds trivial until you count them: every EC2 instance with an auto-assigned public IP, every Elastic IP, each public load balancer node and each NAT gateway has one. Fifty addresses is about $180 a month.
Turn off auto-assign public IP on subnets that do not need it, release Elastic IPs that are not attached, and use the Public IP Insights view in VPC IP Address Manager to list every address in the account.
Idle and forgotten resources Stopping an EC2 instance stops the compute charge but not the EBS volumes or Elastic IPs attached to it. Terminating an instance can leave volumes behind if delete-on-termination was off. Snapshots from old AMIs and one-off backups pile up for years. These commands, run per region, find the usual suspects:
# EBS volumes not attached to anything
aws ec2 describe-volumes --filters Name=status,Values=available \
--query 'Volumes[].[VolumeId,Size,VolumeType,CreateTime]' --output table
# Elastic IPs not associated with an instance or interface
aws ec2 describe-addresses \
--query 'Addresses[?AssociationId==null].[PublicIp,AllocationId]' --output table
# Your snapshots, oldest first
aws ec2 describe-snapshots --owner-ids self \
--query 'sort_by(Snapshots,&StartTime)[].[SnapshotId,VolumeSize,StartTime,Description]' \
--output table
# Log groups that never expire
aws logs describe-log-groups \
--query 'logGroups[?retentionInDays==null].[logGroupName,storedBytes]' --output tableLoad balancers with no healthy targets still bill hourly. An Application Load Balancer costs $0.0225 per hour plus capacity units before it serves a request. A stopped RDS instance starts again automatically after seven days, so stopping a database is not a way to park it. Take a final snapshot and delete it instead. CloudWatch log groups keep data forever by default. Ingestion is $0.50 per GB, and storage keeps adding up. Set a retention period with aws logs put-retention-policy --log-group-name NAME --retention-in-days 30, or whatever your audit needs require. S3 incomplete multipart uploads and old object versions are billed as storage but do not show up when you list a bucket normally. A lifecycle rule with AbortIncompleteMultipartUpload and NoncurrentVersionExpiration handles both. gp2 volumes cost more per GB than gp3 ($0.10 against $0.08 in us-east-1), and gp3 includes 3,000 IOPS and 125 MB/s as a baseline. Most gp2 volumes can be modified to gp3 in place without downtime. Confirm with the owner before deleting anything. We tag candidates first, wait a week, then snapshot and remove. A volume nobody can explain is sometimes the only copy of something.
Charges that arrive with time Some costs appear only because nothing changed. Amazon RDS charges Extended Support per vCPU-hour once a database engine major version passes the end of standard support, and Amazon EKS charges $0.60 per cluster-hour instead of $0.10 for clusters on a Kubernetes version in extended support. Both arrive automatically. Keeping engine and cluster versions current is maintenance work, but it is also cost control.
Savings Plans and Reserved Instances can also turn into a hidden cost in reverse. A commitment bought for an instance family you have since moved away from keeps billing until its term ends. Review utilisation in Cost Explorer before buying more.
A monthly cost check Group the bill by usage type and compare with last month. Investigate any line that grew more than you can explain. Make sure S3 and DynamoDB gateway endpoints exist in every VPC with a NAT gateway. Run the idle-resource commands above in every region you use, not just the default one. Check that every log group has a retention period and every bucket has lifecycle rules. Count public IPv4 addresses and release the ones you do not need. List database and Kubernetes versions approaching end of standard support. Keep AWS Budgets and Cost Anomaly Detection alerts going to named people, not a shared inbox nobody reads. If the same surprises keep coming back, or nobody on the team owns the bill, that is a sign the cost review needs to be part of someone's regular operations work rather than a once-a-year clean-up.
Infigini IT Services · 11 Kanwali Road, Dehradun 248001, India · info@infigini.com · +91 95684 87789