Egress Cost Optimization for S3-Heavy Architectures
Hidden data-movement costs turn the real bill into a multiple of the storage rate itself.

Every team that stores data in S3 eventually hits the same moment: the invoice lands, and it's nowhere near what the storage rate suggested it would be. This isn't an accounting mystery; it's the predictable result of how the system was built.
The storage rate is the number teams plan around. But the actual invoice runs as a multiple of that number, because egress, cross-AZ transfer, cross-region transfer, and request fees never appear in the headline rate. None of this is hidden. What drives the gap is the default shape of most architectures: compute sitting in one place, storage in another, often split across regions or even across clouds. That split means data crosses a billing boundary every time something needs it.
Optimizing the bill without touching the architecture is a treadmill. The useful question isn't "how do I pay less per GB transferred." It's "how do I design things so fewer GB cross a billing boundary in the first place." That shift in framing is what the rest of this piece builds on.
The four billing boundaries where data movement costs accumulate
Egress is four separate billing boundaries, each tripped by a different architectural choice, and most real-world setups manage to cross several of them at once.
Internet egress covers data leaving AWS for the public internet or for any non-AWS service. When data moves between Availability Zones in the same region, it triggers cross-AZ transfer. NAT Gateway processing is the fourth: EC2 instances sitting in private subnets that reach S3 through a NAT Gateway pay a processing fee of $0.045 per GB on top of whatever egress charge already applies. A free VPC Gateway Endpoint for S3 wipes this charge out completely, making it one of the simplest fixes available anywhere in this list.
Stacking these together makes the real cost of a request clear. A multi-AZ, multi-region application built without much thought can cross three of these four boundaries in a single transaction, pushing the effective per-GB cost well past what any one published rate would suggest. Transfers between S3 and AWS compute (EC2, Lambda, EKS) inside the same region cost nothing. That single free lane is the reason co-location matters so much, and it's the thread the next few sections pull on.
AI training workloads and structural egress cost
AI training and inference workloads take this problem and put it under a magnifying glass, because the same data gets pulled over and over, and the files involved (datasets, checkpoints, model weights) tend to be enormous.
Training datasets get read repeatedly across epochs. Every full pass across the data triggers egress if compute and storage don't live in the same place, and training runs don't do one pass, they do dozens. Model checkpoints make this worse. They're large, and they have to be loaded and saved constantly during a run. If those checkpoints are in S3 while training runs on compute in a different region, or a different cloud entirely, every restart becomes a billable transfer.
This sets up what's known as the neocloud migration trap. So a team moves its GPU compute to a different cloud provider chasing better economics on the compute side, but it leaves the training data sitting in S3. The savings from the cheaper GPUs get eaten, partly or entirely, by the cross-cloud egress bill generated every time that compute reaches back for the data it needs.
Inference serving adds its own layer on top. All of it runs at standard egress rates, with no special AI discount anywhere in the pricing.
None of this means AI workloads are unusually data-hungry by nature. The real issue is that separating storage from compute, a pattern that's convenient to build and easy to reason about, turns financially painful the moment data volumes get large and access gets frequent. AI training just happens to be the workload that hits both conditions at once.
Co-location is the highest-leverage architectural fix
So if you put compute in the same AWS region as the S3 bucket it reads from, the largest per-GB charges disappear outright, because same-region transfer between S3 and compute costs nothing. This is a category of charge disappearing entirely, not a tweak at the margins.
Things get more complicated for teams that have already moved GPU compute to a non-AWS provider chasing better pricing. Those teams have to decide whether the training data should move to follow the compute, or whether a storage layer closer to that compute should get introduced instead.
Co-location isn't only a region-level decision. It applies at the Availability Zone level too. Keeping compute and storage in the same AZ removes cross-AZ transfer charges, but it comes at the cost of reduced redundancy. That's a real trade-off, and it should be a choice someone made on purpose.
One more fix belongs here, and it's close to free money: the VPC Gateway Endpoint for S3. It routes traffic from EC2 instances in private subnets straight to S3 over AWS's private network, so NAT Gateway processing fees disappear completely, and you pay nothing to set it up. It won't fix cross-region transfer costs by itself, but it closes one leaky pipe for basically zero effort.
Caching as an architectural pattern for reducing egress volume
Co-location works when compute and storage can share a home. When they can't, because compute sits somewhere remote, users are scattered across the globe, or access patterns are too unpredictable to plan around, a caching layer sitting between S3 and whoever needs the data cuts the number of billable retrievals, often by a wide margin.
The mechanism is simple. A cache placed in front of S3 absorbs repeated reads of the same object, so the billable egress event happens once, at cache fill, instead of once for every single request that follows.
If content reaches distributed users, you can route delivery through CloudFront instead of straight from S3: the S3-to-CloudFront hop carries no charge, because transfers within the same account are free, and CloudFront's edge network brings down the per-GB cost of that final leg to the user. This is a well-worn pattern, and it delivers a serious cost reduction for any public-facing workload with real traffic.
For AI training, caching frequently used dataset shards or checkpoints on local NVMe storage attached directly to the training nodes removes the per-epoch egress cost after that first pass. CoreWeave's Local Object Transport Accelerator, known as LOTA, is a named example of exactly this pattern, built as an intelligent caching proxy running on GPU and CPU nodes, and it applies across training, inference, and data-pipeline workloads, not training alone.
For inference, caching model weights directly on the serving node instead of pulling from S3 on every cold start removes a category of egress that scales right along with how often the service restarts and how big the model is.
This pattern is visible in filesystem layers that mount S3 as a POSIX filesystem backed by an NVMe cache: reads come from the cache at sub-millisecond speed, and S3 only gets touched on a cache miss. That makes the caching invisible to the application. Existing code keeps running without changes, the bucket stays the single source of truth, and the number of billable S3 GET requests drops sharply.
Caching does bring its own trade-offs. But the workloads where egress hurts most, large datasets read over and over, model weights, media assets, tend to be written once and read constantly. Caching works best on exactly that access pattern, and staleness is least likely to bite there.
Access pattern alignment: matching S3 storage class to actual read behavior
Storage tier is the third lever, and getting it wrong is its own kind of architectural mistake. You fix it by understanding how the data actually gets read, not by guessing.
The price gap across S3 storage classes is wide. Standard costs far more per GB than Glacier Deep Archive. A team shifts data to a cheaper tier to shrink the storage line, and then an unplanned retrieval, a compliance request, a retraining run, a debugging session, generates a retrieval fee big enough to wipe out months of savings in one shot.
S3 Intelligent-Tiering exists to take the guessing out of this decision for objects with access patterns that aren't predictable. It shifts objects between tiers automatically based on observed access, it skips retrieval fees for the Frequent and Infrequent Access tiers, and it charges a small per-object monitoring fee that pays for itself once objects are large enough. The exception is small objects under 128KB, where that monitoring fee can end up costing more than the tiering saves.
AI workloads have a clean rule to follow here. If datasets get read repeatedly during an active training run, they belong in Standard, or cached locally the way the previous section describes. Those same datasets, once archived between training runs, are good candidates for Standard-IA or Glacier Instant Retrieval. So you build lifecycle policies that shift data between tiers based on the training schedule itself, so things don't stay parked in Standard indefinitely because nobody wrote the policy.
Two quieter cost drivers deserve attention too. Incomplete multipart uploads work the same way: partial uploads that never finished, or never got aborted, keep billing at Standard rates indefinitely until a lifecycle rule steps in to clean them up.
Provider alternatives and zero-egress architectures: when the structural fix is leaving S3
For some teams, co-location, caching, and tier alignment inside S3 won't fix it. For those teams, the structural fix is leaving S3. Zero-egress providers are a legitimate option when egress dominates the bill and deep AWS ecosystem integration isn't a hard requirement, but switching providers isn't simply egress costs running in reverse.
Cloudflare R2 charges no egress fees at all, and that model has real traction in AI distribution.
OVHcloud announced it was removing egress fees from its Object Storage offering entirely, so all outgoing public traffic across every Object Storage class and every region is covered, and the change shows up on the January 2026 invoice covering December 2025 usage.
Regulation is pushing in the same direction. So the EU Data Act bans switching-related egress charges starting January 12, 2027, under Article 29 in Chapter VI. That ban only covers egress tied to switching providers, not everyday operational egress, so it closes off exit-fee lock-in but doesn't touch the ongoing cost of running workloads across multiple clouds in parallel.
The strongest case against zero-egress providers as an easy win: a provider can charge nothing for egress and still lock a team in through proprietary APIs, partial S3 compatibility, slow data export, or a missing set of managed services that plug natively into S3. The deeper lock-in is the whole ecosystem of AWS services built to read from S3 directly, not the per-GB egress rate. That's why S3 API compatibility has become close to a requirement for any alternative hoping for real adoption. AI frameworks, data pipelines, and cloud-native tooling are all built around the S3 API, and an alternative without full compatibility runs into adoption friction no matter how good its pricing looks.
The practical way to decide: teams where egress eats up a large share of the total S3 bill, where AWS managed service integration is minimal, and where the main use case is data distribution or feeding training data across clouds, are good candidates for migrating providers. If a team is deeply woven into AWS's managed services, the egress savings often get offset by the work of rebuilding that integration somewhere else. A middle path exists too: a storage layer that's S3-compatible and can mount S3, GCS, R2, or Azure Blob without migration or ETL work lets a team pick the cheapest underlying store for each workload, without rewriting application code and without betting the whole architecture on one provider's fee structure.
Applying the architectural audit: how to read your S3 bill as a map of design decisions
The real value in an S3 cost breakdown isn't a list of line items to trim. Each charge on that bill is evidence of a design decision, a co-location gap, a missing cache, a tier mismatch, or the wrong egress path, and each one can be fixed at the source.
If internet egress charges run large relative to storage costs, data is being served straight from S3 to users or to systems outside AWS, and you have to ask whether a CDN, a cache, or a co-located replica should be absorbing that traffic instead. If NAT Gateway processing charges appear alongside S3 request costs, compute sits in private subnets without a VPC Gateway Endpoint in place, so check that first, because it's the cheapest fix in this entire piece. When storage charges run high relative to egress, with most of the data parked in S3 Standard, that points to tier misalignment, which you fix by measuring actual access patterns through S3 Storage Lens and S3 Analytics. Versioning-related storage growth and incomplete multipart upload charges both trace back to missing lifecycle policies, and the fix there is a policy, not a negotiation with AWS billing. For AI teams, a large checkpoint-related egress line item means you have to ask whether checkpoint storage sits in the same place as the training compute, and whether a local NVMe cache is catching repeat reads or every epoch still reaches back to S3 over the network.
Reading the bill this way turns the conversation from "how do we shrink this number" into "what design decision produced this charge, and does that decision still make sense." That second question is the one that leads to savings that hold up over time, instead of a diet that has to be repeated every billing cycle.


