How-to guide

Lower NAT and outbound traffic costs on AWS

Find the NAT gateways and outbound paths that quietly add cost on AWS, then route service traffic to endpoints and keep cross-AZ data where it belongs.

By the Cloudverg engineering team · · 6 min read

Why this matters

NAT Gateway billing has two parts: an hourly charge for each gateway and a per-GB charge for data processed. The hourly part runs whether traffic flows or not. The per-GB part grows with the number of private subnet workloads reaching the internet and other networks.

The quiet cost is cross-AZ. If a subnet's route table sends 0.0.0.0/0 to a NAT gateway in a different Availability Zone, you pay data transfer between zones on top of NAT processing. That often appears after a failover, when someone consolidates gateways, or when a new subnet copies an old route table.

Outbound internet data transfer is separate again. NAT processing applies when traffic leaves through the gateway, and standard data transfer out rates apply when it reaches the internet. Fixing one line item without looking at the whole path leaves money on the table. The work is mostly inventory and routing, not a rewrite.

Before you start

You need visibility before changes. Turn on VPC Flow Logs for the VPCs with NAT gateways and deliver them to S3. Keep a couple of weeks of logs. They show source, destination, bytes, and the NAT gateway network interface, which is the only reliable way to see what actually uses the gateway.

In Cost Explorer, group by usage type and filter for NatGateway. You will see NatGateway-Hours and NatGateway-Bytes. Group by resource tag or account to find the owner. If tags are missing, add a cost allocation tag to every NAT gateway before you start.

Pull the route tables for every private subnet. Note which NAT gateway each default route points to and which Availability Zone that gateway sits in. A spreadsheet with subnet, AZ, route target, and gateway AZ is enough. Most surprises show up here.

Check whether workloads use S3, DynamoDB, ECR, CloudWatch Logs, SQS, Secrets Manager, or other AWS services over public endpoints. Those calls commonly dominate NAT bytes. Also note third-party APIs with large request or response bodies, and container image pulls. Those are candidates for caching or private connectivity.

How to do it

Start with the free wins: gateway endpoints for S3 and DynamoDB. They have no hourly charge and no per-GB processing charge. Add the endpoint, then add routes for the AWS prefix list in every private route table. Confirm the SDK still resolves the regional service name and that traffic no longer appears on the NAT gateway.

Interface endpoints cost hourly per network interface per AZ plus a per-GB processing charge. That charge is usually lower than NAT processing for the same volume, and traffic stays inside the VPC. Turn on private DNS so the SDK resolves to the endpoint. Match endpoint AZs to client AZs, or you trade NAT cross-AZ charges for endpoint cross-AZ charges.

Gateway consolidation is a trade. One NAT gateway in one AZ removes duplicate hourly charges but sends all cross-AZ traffic over the regional network, which is billed. Model it for each VPC: compare hourly savings against estimated cross-AZ gigabytes. Small and dev VPCs often win with a single gateway. Busy production VPCs with tightly coupled multi-AZ services usually cost less with per-AZ gateways.

For low-volume or test workloads, a NAT instance on a small instance type can replace a gateway. You own patching, scaling, and failover, so it suits batch jobs and sandboxes, not critical paths. For IPv6, use an egress-only internet gateway instead of routing IPv6 through NAT.

  1. Inventory every NAT gateway and route. List each gateway with its VPC, AZ, attached route tables, and bytes processed. Tag it with owner and environment. This baseline tells you which gateways are idle and which carry real traffic.
  2. Add gateway endpoints for S3 and DynamoDB. Create a gateway endpoint for each service in every VPC. Add the endpoint routes to every private route table. Verify from a private subnet that the service resolves and that NAT bytes for that service fall.
  3. Fix cross-AZ default routes. Compare each private subnet's AZ with the AZ of its NAT gateway. Where they differ, repoint the route to a gateway in the same AZ, or accept the trade and document it. Do this before consolidating anything.
  4. Place interface endpoints in the right AZs. Create interface endpoints in the AZs where clients run. Enable private DNS. Start with the services that dominate NAT bytes, such as ECR, CloudWatch Logs, SQS, and Secrets Manager.
  5. Test a single gateway in non-production. Pick one non-production VPC and remove duplicate NAT gateways after route changes. Watch cross-AZ transfer and application latency for a week. Keep the result as a pattern for similar VPCs.
  6. Cache large downloads. Put a package proxy or container registry cache close to the workloads. Pull base images through an ECR pull-through cache. Cache vendor API responses where the contract allows. Fewer bytes through NAT means lower processing charges.
  7. Add alerts and dashboards. Create a CloudWatch dashboard for NatGateway BytesOutToDestination and BytesOutToSource per gateway. Set a budget alert on NatGateway-Bytes and on data transfer. Review weekly, not monthly.

How to check it worked

Compare NatGateway-Bytes in Cost Explorer week over week. A drop after a gateway endpoint usually shows within a day. Check the NAT gateway CloudWatch metrics split by gateway. If total bytes drop but one gateway's share rises, traffic is consolidating, which may be intended.

Check the endpoint metrics. Interface endpoints publish traffic metrics. If an endpoint shows near-zero traffic, private DNS is probably not resolving. From a private subnet, query the regional service name and confirm the answer is a private address.

Review the cross-AZ data transfer line in Cost Explorer. If it rose after consolidation, the hourly savings may be smaller than expected. The bill tells you quickly.

Run the route table audit again. Confirm every private subnet still has a default route, the endpoint routes exist, and no new subnet was created without them. Repeat after each infrastructure change.

Mistakes to avoid

Creating an interface endpoint in one AZ and pointing clients from three AZs at it. You pay cross-AZ transfer for every request and lose AZ resilience. Match endpoint AZs to client AZs.

Forgetting private DNS on interface endpoints. The endpoint exists, the routes are right, and the SDK still calls the public address because the VPC DNS settings were not enabled. Turn on DNS resolution and the private DNS name.

Deleting a NAT gateway without checking route tables. Subnets lose outbound access and health checks fail. Update or remove routes first, then delete in a maintenance window.

Treating NAT gateway cost as one number. Split hours and bytes, and split by AZ. Fixing bytes while idle gateways keep billing hourly is half a job. Also compare private paths: VPC peering, Transit Gateway, and PrivateLink have their own charges, but a public NAT round trip is rarely the cheapest way to reach another VPC.

If mapping all this by hand is too slow, the Cloudverg Cloud cost audit at $4,500/month is one option. It is a two-week read-only review that includes data transfer and egress findings.

Frequently asked questions

Why is my NAT gateway bill high when traffic is low?

The hourly charge per gateway runs with or without traffic. Accounts often keep one gateway per AZ per VPC, including unused AZs. Delete or consolidate idle gateways after you check route tables.

Are VPC gateway endpoints free?

Gateway endpoints for S3 and DynamoDB have no hourly or per-GB charge. You add routes to the AWS prefix list in private route tables. Interface endpoints are different and do have hourly and per-GB charges.

Should I run one NAT gateway or one per AZ?

One per AZ is the resilient default, but each gateway bills hourly. A single gateway saves hourly fees and adds cross-AZ transfer for clients in other zones. Compare the two for each VPC using real flow log bytes.

Monthly plan

Want help with this? Cloud cost audit

$4,500/month

2 weeks · Starts within 10 business days

A two-week, read-only review of your AWS, Azure, Google Cloud or Oracle bill, with every saving ranked by dollars per month.

Renews monthly. Cancel any time.
What is included

Related guides

More cloud costs guides   Cloud cost audit

Smallest way to start

Get a senior architect on your problem this week

Ninety minutes a month on the cloud, AI or security question that is blocking you, a pre-read of your material and a written summary within two business days.

$450/month

Renews monthly. Cancel any time.