Understanding Data Egress Cost When You Migrate Off AWS
September 2026 · 18 min read · Surya

- Data egress cost is not one fee, it is a billing decision made per byte three times over
- The free tier is real, and it is the only part of the AWS egress bill that is not priced per byte with a margin attached
- S3 API requests, cross-AZ traffic and early deletion are egress-adjacent charges that appear on a migration bill and not on a diagram
- A control plane that never stores file contents and never meters migrated bytes changes the arithmetic of leaving
- The egress fee is the visible cost; the lock-in is the one that does not appear on any invoice
The line item called data transfer out is not the cost of leaving AWS. It is one of several costs, and it is usually not the largest.
The rest are API request charges, cross-AZ traffic, early deletion and minimum storage duration, plus the operational cost of the migration itself. All of it is a pricing model, not a physics constraint. Bytes over a wire cost the same to move whether they leave an AWS region or a rack in your office. The invoice is a design choice. Somebody chose it. You can choose differently.
That is what "data egress cost" actually is: a design artifact you can plan around, argue with, and in some cases route away from entirely. This post is about how the mechanism works, which charges hide behind the visible one, and where we at Storafleet sit in that picture, including the places we lose.
Data egress cost is not one fee, it is a billing decision made per byte three times over
The posted per-GB rate for internet egress is a floor, not a total. People screenshot it, multiply by their bucket size, and think they have an estimate. They have a lower bound on one of several charges.

Take a single object store and walk it. The same bytes can be billed on the way out over the internet, on the way to another AZ, on the way to another region, on the way into a different storage class, on every API call that touches it, and on deletion before a tier's minimum retention window closes. Each of those has a different governing mechanism. Some are per-GB. Some are per-request. Some are per-object-per-day. Some are time penalties.
Internet egress is the one everybody talks about, so let's get it out of the way. Data leaving an AWS region toward a public endpoint is billed per GB after a small free allotment, in tiers. The rate drops as volume rises. The rate has changed repeatedly over the years, and it varies by region and by whether you are using a CloudFront distribution or a direct internet path. We are not going to quote a number as fact because the number is not the point and it will not be the same when you read this. The point is the shape: per-GB, tiered, metered, and only one of several meters running.
Cross-AZ transfer is the charge that surprises people who thought they were already inside the cloud. An EC2 instance in one availability zone reading from an S3 bucket whose data is served from another AZ can incur transfer charges. Multi-AZ architectures are sold as the reliability default, and they are, and they also create a permanent low-level bill for data moving between zones that you will never see on an architecture diagram. It is not internet egress. It is not free. It is the same underlying resource (a network) metered differently because the pricing model says so.
Cross-region replication is the third distinct charge, and it is the one that people enable for compliance or latency and then forget. Every object written to a replicated bucket is transferred to the destination region and stored there. You pay transfer out of the source region plus storage in the destination plus request charges at both ends. If your replication rule catches a bucket that receives a lot of small writes, the request charges can outrun the transfer charges. Nobody puts that on a diagram either.
Then there is the request layer. S3 bills per thousand requests, with different rates for different operations. PUT, GET, LIST, HEAD, and the multipart variants all get counted. This is not egress in the classic sense. It is egress-adjacent, and it lands on the same invoice, and for a migration it can be the difference between a bill that matches your estimate and one that does not.
And finally the retention mechanics. Infrequent access tiers have minimum storage durations. Delete an object before the window closes and you pay the remainder. Some tiers have retrieval fees and minimum object sizes, where a 4 KB object is billed as if it were 128 KB. Lifecycle policies that transition objects to cheaper tiers can create early deletion charges if a later policy moves them again too soon. These are not transfer costs at all. They are the storage product's way of pricing the risk that you use the cheap tier the way it was designed. They belong in the migration estimate anyway, because a migration involves a lot of reading and a lot of deleting.
Four to five mechanisms, one line item called "egress", one number on a slide. That is the actual structure. Anyone quoting a single per-GB figure to describe the cost of leaving is either simplifying on purpose or has not done it.
The free tier is real, and it is the only part of the AWS egress bill that is not priced per byte with a margin attached
AWS publishes a monthly free allotment for data transfer from its services to the internet. That is real, it applies across most services, and it resets every month. If your workload moves less than that out to the public internet, your internet egress bill is zero and has been for years.
Past that, tiered per-GB pricing applies. The tiers are published, they change, and they are region-dependent. We are not going to state a current rate or a current free-tier size as fact here, partly because it will drift and partly because the mechanism is more useful than the number. Every band you cross gets billed at that band's rate, so the bill is the sum of slices, not a flat multiple.
Worked example, round numbers, illustrative only. Say a bucket holds 12 TB and you want all of it out to the internet. A slice at the bottom is free. The rest falls into tiers. If the first paid tier covers, say, the next 10 TB and a higher band covers the rest, your bill is roughly 10,000 GB at the first rate plus 1,900 GB at the second. Both rates are the provider's current published numbers, not ours, and they are the thing to look up on the day you plan the move. The shape of the arithmetic is the part that does not change.
The free tier is not a discount. It is a customer acquisition mechanism, and it is the only part of the AWS egress bill that is not priced per byte with a margin attached. The allotment is small enough that almost every production workload blows past it, and large enough that almost nobody notices the first month. That is not an accident.
Here is the trap in the free tier, and it is worth understanding before you plan anything. The free allotment applies to transfer to the internet. It does not apply to cross-AZ transfer, which is a separate meter. It does not apply to cross-region replication. It does not apply to request charges. So a workload that moves its free allotment to the public internet for free can simultaneously be paying thousands of dollars a month to move data between AZs and regions inside the same account. The free tier answers one question. People treat it as if it answered all of them.
If you are sizing a migration, the free tier is a rounding error and you should plan as if it does not exist. If you are running a small app and wondering whether egress is why your bill is high, check the other meters first. It usually is not the internet egress.
S3 API requests, cross-AZ traffic and early deletion are egress-adjacent charges that appear on a migration bill and not on a diagram
A migration tool that reads a bucket generates API calls before it moves a single byte. This is the part that nobody models, and it is the part that produces the invoice surprise.
Walk through what a naive migration does. It lists the bucket to discover objects. If the bucket has a flat namespace and the tool is not careful, that is paginated LIST calls at 1,000 keys per page. A hundred million objects is a hundred thousand LIST requests before you have downloaded anything. Then for each object, if the tool needs metadata or a checksum, that is a HEAD. Then the GET itself, which is where the bytes move, and which is metered per request as well as per GB. Then the write on the destination side, which is a PUT on a different provider with a different pricing model.
If the tool is multithreaded, which it should be, all of this multiplies by concurrency. If it retries on failure, which it must, the retries are billed too. A sync that fails halfway and restarts re-reads the listing. None of this appears on the architecture diagram that says "migrate S3 to R2".
Here is the specific case that catches people: object versioning. A versioned bucket does not contain objects. It contains versions. If a key has been overwritten 40 times, a naive migration moves 40 versions and bills 40 GETs, unless the tool understands versioning and you have decided which versions you actually want on the other side. Most people have not decided. Most people discover the question when the bill arrives.
Object lock and retention add another layer. Legal holds, governance mode, compliance mode. A destination provider may not support the same lock semantics as the source, so the migration tool has to either drop the lock (which may violate your compliance posture) or fail the object. Both outcomes have costs, and neither is the per-GB egress rate.
Early deletion is the third category. If your objects are sitting in an infrequent access tier with a 30 or 90 day minimum, and your migration reads them and then you delete the source bucket, you pay the remainder of the minimum storage duration on every object. That can be a substantial fraction of the source bucket's monthly storage bill, multiplied by however many months remain on the clock. Lifecycle policies that transitioned objects into the tier recently make this worse, not better.
Cross-AZ is the fourth. If your migration reads from an EC2 instance or a Lambda in the same account, and S3 serves from a different AZ, you pay transfer between zones on top of everything else. If you read from outside AWS entirely, you pay internet egress instead. There is no path that avoids both.
These are the charges that "cloud egress costs" and "data egress cloud costs" actually refer to when people use the phrase in a planning meeting. The per-GB internet rate is the visible one. The request charges, the cross-AZ traffic, the early deletion penalties and the listing overhead are the ones that turn a $4,000 estimate into a $9,000 bill, and they are all governed by different rules.
If you want the full migration walkthrough including how we handle these at Storafleet, it is at storafleet.com/migrate-off-aws. Read it before you commit to a plan, not after.
A control plane that never stores file contents and never meters migrated bytes changes the arithmetic of leaving
Storafleet is a control plane over object storage you already own. That sentence determines every cost property that follows from it.
We do not store your file contents. Not as a cache, not as a staging area, not as a temporary buffer. When you connect two S3-compatible providers and run a migration, the bytes move between them. They do not land on our infrastructure. This is not a marketing position. It is the reason we can price the way we do: we are not paying for storage or bandwidth on your behalf, so we do not have to bill you for it.
We connect 50+ S3-compatible providers and any custom S3 endpoint. Amazon S3, Cloudflare R2, Backblaze B2, Wasabi, MinIO, DigitalOcean Spaces, Oracle Cloud, IBM Cloud Object Storage, Scaleway, Linode, Vultr, Storj, IDrive e2, Hetzner Object Storage. Bring your own buckets, connect them, move data between them.
On pricing: Storafleet never charges per gigabyte to move data. Migrated bytes are unmetered on every tier, including the free one. Tiers differ on concurrent migration jobs and on automation, specifically scheduled sync. That is the whole distinction. A 40 TB migration costs the same as a 40 GB one in terms of what we charge you, because we are not in the bandwidth business.
Compare that to the shape of the problem in the previous section. The listing overhead, the request charges on the source side, the cross-AZ traffic, the early deletion penalties: those are real and they are yours regardless of tooling, because they belong to the source provider's pricing model. What we remove is the destination side of the meter and the tooling side of the meter. We do not add a per-GB charge on top of what you are already paying to leave.
A scheduled sync definition looks like this:
{
"name": "nightly-source-to-destination",
"source": {
"provider": "aws-s3",
"bucket": "prod-assets",
"region": "us-east-1",
"auth": "iam-role-assumption"
},
"destination": {
"provider": "cloudflare-r2",
"bucket": "prod-assets-mirror",
"auth": "access-key"
},
"schedule": "0 3 * * *",
"mode": "incremental",
"options": {
"skip_versions": true,
"detect_duplicates": true,
"max_concurrent_transfers": 32,
"on_error": "retry_then_report"
}
}
Two things worth pointing at. skip_versions: true is how you avoid the versioning trap from the last section: incremental sync moves current state, not the full history, unless you decide the history matters. And auth: "iam-role-assumption" means the AWS side can be connected without storing a long-lived access key at all, which is the strongest credential posture we can offer for AWS specifically.
Which brings us to the concession, and it is a real one.
rclone is free, permanently, and for a large number of people reading this it is the honest answer. We are not going to pretend otherwise. It supports 70+ backends, including the consumer clouds we deliberately do not touch. It mounts storage as a filesystem. It is scriptable, which means it composes with cron, systemd, CI pipelines, shell scripts and Makefiles in ways a web console never will. It runs inside your own security boundary, so credentials never leave hardware you control. It has been maintained for over a decade by people who know what they are doing.
Where the free tool genuinely beats us: any workflow that is code. Any workflow where the credential must never leave your machine. Any workflow that needs Google Drive, Dropbox, OneDrive, SFTP or WebDAV, because we do not speak those protocols at all. Any workflow that needs a mounted drive in Finder or Explorer, because we have no FUSE surface and cannot give you one. Any workflow where the budget is zero, because it costs nothing and we do not.
Where we fit instead: a team with buckets on several providers who wants a console, scheduled sync without writing a cron wrapper, cross-bucket search across providers, duplicate detection, and per-bucket cost visibility in one place. That is a real workflow the command-line tools do not target. But if you read the previous paragraph and thought "that's me, I'd just write the script", you should write the script. The conversation is over and rclone wins.
There is one more honest limit. Your credentials sit encrypted in our database, not on your own hardware. The encryption is real: AES-256-GCM with per-namespace HKDF keys, and AWS can be connected keylessly via IAM role assumption so no long-lived key exists for that provider. But "the keys never left my laptop" is a stronger answer than ours, and it would be dishonest to pretend the gap is smaller than it is. If that sentence is a requirement for you, rclone or Mountain Duck is the right tool and we are not.
The egress fee is the visible cost; the lock-in is the one that does not appear on any invoice
Every migration you do in the future re-incurs the egress cost of the one you are doing now, unless you change something structural. That is the part the invoice does not show.
Move off AWS to provider X today. Pay the egress. Fine. In three years, provider X raises prices, or gets acquired, or drops a feature you depend on, or you decide the latency to a new market is wrong. You migrate again. You pay egress again, from X this time, and you pay it on whatever the data has grown to. The fee you paid the first time was not a one-off cost of leaving. It was the first installment of a recurring cost of being in a system where leaving is metered.
This is not an argument against ever moving. It is an argument for thinking about the exit before you need it. If provider X has no egress fee, the second move is cheaper than the first, and the third is cheaper still. That is the actual structural difference between a provider that meters egress and one that does not, and it compounds over years in a way that no single migration estimate captures.
The second layer of lock-in is API drift. "S3-compatible" is a spectrum, not a boolean. Every provider implements a slightly different subset, with slightly different semantics for multipart uploads, presigned URLs, versioning, object lock, lifecycle rules, tagging, and error codes. A migration tool that only speaks one dialect works beautifully until it hits the provider that returns a different error shape for the same condition, and then it fails silently or fails loudly, and either way you are debugging it at 2am.
Versioning and object lock are where this bites hardest. AWS S3 versioning semantics are the reference implementation. Other providers approximate them. Object lock in governance mode versus compliance mode, retention periods, legal holds: these are the features you enabled for a compliance reason, and they are the features most likely to not survive a move intact. A tool that understands the semantics can tell you what will be lost before you start. A tool that treats every provider as the same S3 will move the bytes and quietly drop the guarantees.
The third layer is operational. A migration tool that only speaks one provider's dialect is a tool you have to replace every time you change providers. A tool that speaks the general case is a tool you keep. This is the part that never shows up on an invoice because it is measured in engineering hours and incident retrospectives, and those are harder to put in a spreadsheet than a per-GB rate. They are also usually larger.
So the full answer to "what does data egress cost" is: the per-GB fee you can look up, the request and cross-AZ and early deletion charges you have to model, and the recurring structural cost of being in a system where the next move costs the same as this one. The first is visible. The second is knowable. The third is the one that determines whether you are having this conversation again in three years.
Now the verdict, and we mean it.
Do not use Storafleet for this if your budget for migration tooling is zero. rclone is free forever, has no account, and does the job. The conversation is over and rclone wins.
Do not use us if you need storage mounted as a filesystem. We have no FUSE surface. rclone mount, Mountain Duck or ExpanDrive will do it and we will not.
Do not use us if the job involves Google Drive, Dropbox, OneDrive, Box, SFTP or WebDAV. We speak the S3-compatible family and custom S3 endpoints, and nothing else. rclone and MultCloud cover the consumer clouds and we deliberately do not.
Do not use us if your credentials must never leave hardware you control. Ours are encrypted at rest with AES-256-GCM and AWS can be connected keylessly, but rclone runs inside your boundary and that is a stronger answer. Take it.
Do not use us if everything lives in one cloud and always will. That provider's console is free, always current with its newest features, and authoritative in a way we cannot be. Use it.
Do not use us if the entire workflow is code and always will be. A CLI composes with cron, systemd and CI. A console does not. Write the script.
And do not use us if you need a decade of track record. We are a small team and Storafleet started in 2026. We have no published case studies and no named customers, and we will not invent them. On the question "will this still exist in five years", a ten-year-old open source project has a better answer than we do. That is a real risk and you should price it.
Use us if you have buckets on several providers, you want a console instead of a shell script, you want scheduled sync without maintaining a cron wrapper, and you want the migrated bytes to not appear on a meter. That is the specific shape of problem we built for. If it is not yours, the alternatives above are honest answers and we would rather you take one than sign up and be disappointed.