A cloud bill becomes surprising when a technical decision creates a cost that nobody owns or measures. Network paths are a common place for that gap. An application can serve the same number of users while image downloads, backups, retries, or data exports take a more expensive route.
One r/devops discussion about NAT Gateway consuming most of an AWS bill describes that problem clearly. The author reported that NAT represented about eighty percent of their spend and asked whether endpoints or NAT instances would help. That percentage is their account of one environment, not a benchmark for yours. The useful question is how to establish the actual traffic and compare changes before making them.
Start by separating the bill into fixed infrastructure, processed data, transfer, and the cost of operating your chosen design. Then change one traffic path at a time. This guide includes an offline cost model and tests that demonstrate why removing traffic from NAT can reduce one charge while adding another. No paid AWS resources were created for the example.
Download the complete runnable examples, including setup instructions and verification commands.
Identify the charge before choosing a replacement
AWS documents that a NAT gateway incurs availability-hour and data-processing charges. Those are separate from other applicable network costs. The gateway can be idle and still have a fixed charge. Traffic can also incur transfer charges whose origin is the surrounding topology.
Build a short inventory for the period you are investigating. Record the account, Region, workload owner, relevant usage types, deployed NAT gateways, and the amount attributed to each billing category. Keep discounts and credits visible. A disappearing startup credit can change the amount paid without changing the underlying architecture or consumption.
Choose the comparison basis deliberately. The question “what will we pay next month?” may need a different view from “which workload caused the increase?” Preserve the original billing export and write down how you handled credits, taxes, support, and shared infrastructure. Otherwise, two engineers can produce different totals while both are faithfully reading their chosen view.
The goal of this first pass is a bounded hypothesis. For example: a newly introduced image-pull pattern explains additional processing charges in one environment. That is more actionable than a general instruction to reduce the network bill.
Read billing categories and traffic evidence together
The AWS CLI can retrieve a daily breakdown by usage type. This read-only example uses an explicit interval; its end date is exclusive, as described in the Cost Explorer command reference.
aws ce get-cost-and-usage \
--time-period Start=2026-10-01,End=2026-10-08 \
--granularity DAILY \
--metrics UnblendedCost \
--group-by Type=DIMENSION,Key=USAGE_TYPE \
--region us-east-1 \
--output json > daily-usage-costs.jsonThis command was not executed against a live account during authoring. It requires billing access, and API requests can have their own charges. Review the appropriate billing scope before using it in an organization. Inspect the returned usage types rather than assuming every Region exposes an identical spelling or that one service-level total tells the whole story.
Billing establishes the charged categories. Traffic evidence helps explain their cause. VPC Flow Logs can capture interface traffic and deliver records to a configured destination. Select the relevant interfaces and a bounded investigation window, then map source addresses back to workloads and owners.
Do not add every interface's byte count and call the result a reconstructed invoice. A packet may appear at several observation points. Flow records also need the correct field interpretation and collection scope. Use them to find contributors, then reconcile the proposed explanation against billing quantities. Flow-log ingestion, retention, and queries deserve their own budget; leaving a broad investigation configuration running indefinitely can create another cost problem.
Draw the routes that generate the traffic
Classify traffic by destination and purpose. Separate same-Region object storage, registry API calls and image layers, ordinary internet services, and cross-Region transfers. For each group, identify the originating workload, its subnet, the selected route, and which Availability Zones the path crosses.
A destination name is only the beginning. Verify the complete route and every service dependency before changing it.
An architectural diagram should explain a specific request. Follow one artifact download from a private workload to its destination. Repeat the exercise for a normal external API request and a deployment that pulls an uncached image. This makes assumptions about caching, DNS, routing, and dependencies visible to reviewers.
Avoid changing subnet exposure merely because a public route appears cheaper. Reachability, outbound identity, inspection, and security controls are part of the application contract. Price comparisons should preserve the requirements the system actually needs.
Start with eligible gateway-endpoint traffic
Gateway endpoints for S3 and DynamoDB provide an alternative path without requiring a NAT device. AWS lists no additional charge for using these gateway endpoints. They are a distinct endpoint type and do not use AWS PrivateLink.
The scope matters. The route must be associated with the tables used by the workload. Service prefix lists are Region-specific, and traffic for other destinations does not automatically take the new path. Confirm which calls the application makes instead of interpreting an endpoint's existence as proof that the intended bytes moved.
The following Terraform fragment illustrates the association with existing private route tables. It is not a complete module and was not applied.
resource "aws_vpc_endpoint" "s3" {
vpc_id = var.vpc_id
service_name = "com.amazonaws.${var.aws_region}.s3"
vpc_endpoint_type = "Gateway"
route_table_ids = var.private_route_table_ids
}Define an endpoint policy suitable for the real application and retain its IAM and bucket controls. Test allowed and denied operations using the workload's identity. A successful connection is insufficient if the application also needs different actions, buckets, or Regions during a background job.
After rollout, compare the changed processing quantities with the traffic you expected to remove. Keep the fixed NAT component in the calculation if the gateway still serves internet destinations. This prevents an easy accounting mistake: claiming the entire gateway cost as savings when only some of its traffic changed.
Price interface endpoints as a separate design
Interface endpoints have hourly and data-processing charges. Their network interfaces, subnet placement, security groups, DNS configuration, and supported service behavior form part of the design. Adding every available endpoint without measuring demand can increase the fixed floor of a small environment.
The relevant unit is the set of endpoint services deployed across the intended zones. A comparison for one service in one subnet does not describe an environment using several private service APIs across multiple zones. Inventory the required interfaces before multiplying any hourly rate.
Container registries illustrate the dependency problem. The ECR endpoint documentation describes registry endpoint requirements and the S3 path used for image layers. The exact combination depends on the workload and features. Test the uncached-image case and any additional downloads your image needs; a warm cache can conceal an incomplete private path.
Record both the traffic moved away from NAT and the endpoint charges added. A useful review also asks whether the path serves a security requirement that has value beyond a direct reduction in the invoice. Cost is one input to that decision, and its arithmetic should remain explicit.
Use a small model before arguing about savings
The companion example accepts explicit rates and topology. Its numbers are synthetic teaching inputs, not AWS prices, an offer for any Region, or a forecast. Replace them with verified regional inputs and complete usage assumptions before making a real decision.
The illustrative baseline keeps two NAT gateways for 720 hours, processes 1,000 GB, and uses rates of 0.05 per gateway-hour and 0.05 per processed GB. Its subtotal is 122. Moving 500 GB of eligible S3 traffic to the gateway-endpoint path leaves the two NAT gateways deployed and reduces that modeled subtotal to 97.
The same fixture models two interface endpoint services in two zones, with 300 GB moved off NAT. At hypothetical endpoint rates of 0.01 per interface-hour and 0.01 per GB, that alternative totals 138.8. It is more expensive than the baseline under those assumptions. Raising the useful traffic changes the comparison. That is why a low-volume environment and a high-volume one can justify different choices.
import { readFile } from 'node:fs/promises';
import { compare } from './estimate.mjs';
const scenarios = JSON.parse(await readFile('scenarios.json', 'utf8'));
for (const candidate of scenarios.candidates) {
const result = compare(scenarios.baseline, candidate.config, 10);
console.log(candidate.name, result.delta, result.accepted);
}The implementation keeps fixed and processing components separate and validates nonnegative numeric inputs. An additional-cost field lets a reviewer include applicable IP, transfer, logging, and query charges. Leaving it at zero is a deliberate simplification in the lab, not evidence that those charges disappear in production.
Compare NAT instances with the work they introduce
AWS's NAT comparison distinguishes the managed gateway from an instance you operate. Instance sizing, maintenance, patching, capacity, and failover become engineering responsibilities. An hourly instance price alone cannot establish the full replacement cost.
A small, disposable development environment may tolerate different failure behavior from a production application. State that difference explicitly. If an instance fails, who detects it, who restores the route, and how long may outbound connectivity remain unavailable? Include the time required to test that recovery and keep the configuration maintained.
Availability-Zone placement also deserves its own comparison. Consolidating egress can lower one fixed component while adding a cross-zone dependency or transfer costs. Retaining zone-local paths may cost more while satisfying the intended failure boundary. Test routing and failover before presenting either choice as an equivalent replacement.
Route changes can interrupt existing connections. Plan a controlled transition and a rollback that restores the intended path. A lower network subtotal is not a useful result if deployments, authentication calls, or recovery jobs become unreliable.
Put the cost delta in the deployment review
A plan estimate becomes more useful when it compares a proposed change with the deployed baseline. Review the difference alongside the configuration change. Ask whether the traffic estimate came from a measurement, a product forecast, or a placeholder.
The local lab provides a review gate:
node --test estimate.test.mjs
node estimate.mjs scenarios.json --max-increase=10The second command intentionally exits with status two because one illustrative candidate exceeds the allowed increase. The eight tests verify the cost components, traffic allocation, low-volume tradeoff, changed high-volume result, input validation, CLI failure behavior, and cleanup eligibility. They were executed with Node 22.12.0 and require no downloaded packages.
This gate approves assumptions supplied to it; it does not inspect a live invoice or guarantee future demand. In a maintained pipeline, require an explanation for changed inputs and an owner for exceptions. Reconcile the estimate with actual usage after deployment so repeated forecasting errors become visible.
Layer detection with controls on consumption
AWS Budgets supports actual and forecasted notifications, but billing information is delayed. AWS documents updates up to three times per day, usually eight to twelve hours apart, and warns that additional costs can accrue before a notification arrives. A budget alert should therefore have a response plan, not be treated as an immediate spending ceiling.
An alert explains that a threshold was observed. The workload needs its own deliberate response to growing consumption.
Set application-appropriate ceilings on retry amplification, job concurrency, and automatic growth. Decide how the service behaves when a ceiling is reached: queue work, reject excess requests, pause a noncritical export, or escalate to a person. Test that behavior alongside successful requests so a cost control does not silently turn into an unexplained outage.
Budget actions can apply policies or target specified instances, with automatic or approved execution. Review their scope and recovery path carefully. Preventing new provisioning does not stop every existing workload from consuming paid services, and stopping a component can affect unrelated applications sharing its network path.
Retire temporary resources through an owned workflow
An unused development environment still needs a lifecycle owner. Require an environment classification, an accountable team, and an explicit expiry when temporary resources are created. A scheduler should produce a reviewable candidate list before the team considers automatic cleanup.
The companion filter only selects resources marked as stateless, temporary development work with valid ownership and an elapsed expiry. It excludes production, persistent data, invalid dates, and missing owners. It never contacts AWS or deletes anything.
const candidates = expiryCandidates(resources,
Date.parse('2030-01-02T00:00:00Z'));
// [{ id: 'preview-123', owner: 'team-api',
// action: 'request-reviewed-cleanup' }]A production cleanup system needs fresh inventory, dependency checks, authorization, an audit trail, and recovery procedures appropriate to the resource. Deleting a shared NAT gateway because one preview application expired would violate that ownership model. Treat infrastructure dependencies as shared until their actual boundaries are established.
Finish the investigation with evidence that another engineer can reproduce: the original billing interval, the traffic hypothesis, a reviewed cost comparison, the routing change, and the before-and-after workload behavior. Assign an owner to the resulting controls. Preventing the next surprise depends on maintaining those decisions as the application changes, rather than merely celebrating one smaller invoice.




