Skip to main content

Your PaaS Bill Grew a New Line Item: Fly.io's Snapshot Charges, Cross-Region Transfer Fees, and What a Multi-Region Indie SaaS Actually Pays Now

10 min readDora NodaDora Noda
Share
On this page

On January 1, 2026, Fly.io started billing for volume snapshots — a feature that had been free, that runs automatically every day, and that most users never explicitly turned on. The first charges landed on invoices issued in February 2026. For a typical multi-region indie SaaS — one web service, one worker, and a Postgres primary with a cross-region read replica — that's roughly $5.28/month of new spend on a bill that used to be about $34, a 15.5% increase for doing exactly nothing.

That number is small. That's precisely what makes it worth studying. A billing line that appears without a deploy, a config change, or a decision is a different kind of cost than an expensive one you chose — and it sits next to another line most multi-region users have never priced out: cross-region private-network transfer, which on Fly.io has never been free between regions. This post walks the whole bill, line by line, before and after the change, and then looks at what the same workload costs when every line is something you provisioned instead of something you discover.

The Snapshot Line: How a Free Default Became a Meter

Fly.io volumes get automatic daily snapshots with a default retention of 5 days, configurable from 1 to 60 days via --snapshot-retention. Snapshots are incremental — only data changed since the previous snapshot consumes additional storage — and what you're billed for is the stored snapshot size, pro-rated to the hour.

The rate, effective January 1, 2026: $0.08 per GB per month, with the first 10GB free each month. For context, a regular Fly volume costs $0.15/GB-month of provisioned capacity, so snapshot storage is billed at roughly half the rate of the volume it protects.

Here's what that means for our reference database tier — two 50GB volumes (primary and replica), each holding ~30GB of actual data with ~2GB/day of churn:

  • Base snapshot per volume: ~30GB (the full data set)
  • Incremental snapshots in a 5-day window: ~4 × 2GB = ~8GB
  • Stored snapshot data: ~38GB per volume, ~76GB across both
  • Billable after the free allowance: 76 − 10 = 66GB × $0.08 = $5.28/month

The sensitivity here is retention × churn, and it's steep. Bump retention to 60 days for compliance on a churny 100GB volume and stored snapshot data can run to several hundred GB — a $20–40/month line for one volume. The controls exist: set --snapshot-retention deliberately instead of accepting the default, and use --scheduled-snapshots=false on volumes you back up by other means (which Fly's own docs recommend for frequently-updated data anyway, since a daily snapshot is not a real backup strategy).

The point isn't that $0.08/GB is unfair — it's cheaper than the volume itself. The point is that the meter attached to something that was already running.

The Reference Workload, Line by Line

Here is the whole bill for the reference workload — one web service, one worker, and a two-node Postgres (primary + cross-region read replica) — priced in Fly.io's North America/Europe region group, for an organization created after July 18, 2024 (that date matters; see the next section).

Line itemSpec / assumptionFly.io (monthly)
Web serviceshared-cpu-2x, 512MB$4.04
Workershared-cpu-2x, 512MB$4.04
Postgres machines2 × shared-cpu-2x$8.08
Volumes2 × 50GB × $0.15/GB$15.00
Volume snapshots~76GB stored − 10GB free, × $0.08$5.28
Cross-region replication150GB/mo × $0.006/GB (NA↔EU)$0.90
User egress100GB/mo × $0.02/GB$2.00
Total after Jan 1, 2026≈ $39.34
Total before Jan 1, 2026≈ $34.06
Deltasnapshots, no action taken+$5.28 (+15.5%)

One precision note: this before/after delta isolates the snapshot change because a post-July-2024 org's cross-region rates didn't move in January. An older organization on legacy transfer rates has a second delta hiding in the replication line — more on that below.

For comparison, the same shape on Railway, using Railway's published usage rates ($10/GB RAM-month, $20/vCPU-month, $0.15/GB volumes, $0.05/GB egress) and stated assumptions of average utilization:

Line itemAssumptionRailway (monthly)
Web service512MB avg RAM, 0.2 avg vCPU$9.00
Worker512MB avg RAM, 0.2 avg vCPU$9.00
Postgres primary1GB RAM, 0.25 vCPU + 50GB volume$22.50
Postgres replica (2nd region)1GB RAM, 0.25 vCPU + 50GB volume$22.50
User egress100GB/mo × $0.05/GB$5.00
Cross-region private trafficnot documentedunknown
Total≈ $68.00 + unknown

Railway's usage-based model bills average consumption rather than instance sizes, so your real number depends heavily on utilization — the assumptions above are deliberately modest. The comparison isn't "Railway costs 1.7× Fly" (different billing models make that sloppy). The comparison is the last row: on Fly.io the cross-region line is published, small, and computable. On Railway it's a cell I cannot fill from public documentation at all.

The Cross-Region Line: Private Networking Is Only Free in One Region

Every multi-region architecture has a river of internal traffic: WAL streaming to the read replica, queue chatter, cache invalidation. On a private network, it feels free. On Fly.io, between regions, it isn't — and the rate you pay depends on when your organization was created.

Organizations created after July 18, 2024 are on granular data-transfer rates:

Region groupEgress to internetPrivate network, cross-region
North America & Europe$0.02/GB$0.006/GB
Asia Pacific, Oceania & South America$0.04/GB$0.015/GB
Africa & India$0.12/GB$0.05/GB

Same-region private traffic is free, and all inbound transfer is free. Older organizations on legacy rates don't get the discounted private-network tier — cross-region private traffic bills at rates comparable to internet egress.

Run the sensitivity on our 150GB/month replication stream:

  • NA ↔ EU replica, granular rates: 150 × $0.006 = $0.90/month — genuinely negligible
  • Sydney replica, granular rates: 150 × $0.015 = $2.25/month
  • Legacy-rate org, any pair: 150 × $0.02–0.04 = $3–6/month, and a chatty microservices mesh pushing 1TB/month cross-region turns that into $20–40

Two identical architectures, different org creation dates, materially different bills. That fork is itself a hidden billing line: nothing about your app changed, but which rate card you're on was decided by a date you probably don't remember.

And Railway? Railway's flat $0.05/GB egress is 2.5× Fly's NA/EU internet rate, which at least is easy to model. But for cross-region private-network traffic — the load-bearing flow of the multi-region replica pattern Railway itself supports via multi-region replicas — the public pricing docs, the private networking guide, and the pricing FAQ say nothing either way. To be clear: that silence is not evidence of a hidden charge. It's a forecasting gap. You cannot put a number — including $0 — on that line with confidence, and an unpriceable line is arguably worse for capacity planning than an expensive one, because you can't architect around what you can't compute.

Why You Didn't See It Coming: The Anatomy of a Hidden Billing Line

None of this is a scandal. Fly.io announced snapshot billing, published the rate, and gave a free tier. The interesting part is structural — the three properties that make a billing line invisible until the invoice:

  1. The meter attaches to a default. Snapshots run daily whether or not you asked. A charge on an opt-in feature is a decision you made; a charge on a default is a decision made for you. The only action required to start paying was inaction.

  2. The rate schedule forks on facts about your account, not your app. The July 18, 2024 granular-rates cutoff means the same fly.toml, same regions, same traffic produces different bills for different customers. You can't read your costs off your architecture alone.

  3. Docs silence pushes discovery to the invoice. For the one line Railway doesn't document, the only ways to learn the answer are to ask support or run the workload and read the bill. (For Fly, credit where due: the rates above are all on the public pricing page.)

The pattern generalizes beyond these two platforms. Any managed platform's pricing page is a snapshot of an agreement the vendor can amend; the January change is simply this year's cleanest example of a line moving underneath running workloads.

The Self-Hosted Counterfactual: Costs You Provision Instead of Discover

Now put the same workload on infrastructure you own — say, a few Hetzner cloud instances running Kubernetes, provisioned with Cluster API. The bill has the same categories, but every line changes character:

  • Cross-region/internal traffic: Hetzner cloud servers each include 20TB of traffic, and private networks between your nodes cost nothing extra. The 150GB replication stream isn't a line item; it's a rounding error inside an allowance you already own.
  • Snapshots and backups: you run them (CloudNativePG for Postgres point-in-time recovery, Velero or plain object storage for the rest), against storage billed at a flat published rate — on the order of a cent per GB-month — that you sized and scheduled yourself.
  • Compute: fixed monthly instance prices that a comparable multi-node setup lands well under the managed totals above.

The honest trade: you pay in operations time instead of vendor margin. Postgres HA, backup verification, and failure drills are now your job, and pretending that time is free would be exactly the kind of hand-waving this post exists to call out. If those hours are worth more than ~$40–70/month to you, the managed platforms are winning honestly.

But the structural difference stands regardless of where the break-even sits: on a self-hosted fleet, every line was provisioned by you. Traffic allowances, snapshot retention, and disk sizes are inputs you set, at rates you accepted, on hardware you can inventory. A vendor cannot attach a new meter to your defaults, because you own the defaults. The bill can't grow a line item you didn't write.

Read the Diff, Set the Retention, Price the Silence

Three takeaways, in ascending order of effort:

  • Treat pricing pages like APIs and read the diff. The January snapshot change and the July 2024 rate fork were both published; "hidden" mostly means "unread."
  • Set snapshot retention explicitly on every Fly volume today. The gap between the 5-day default and a 60-day setting on a churny volume is the gap between $5 and $40 a month.
  • Treat undocumented costs as forecasting risk. If a platform's docs won't put a number — even $0 — on a line your architecture depends on, get the answer in writing before the architecture depends on it.

Managed platforms are a fine trade for a lot of teams. Just know which lines of your bill are prices — and which are levers someone else is holding.


If you'd rather own the levers: Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own, where traffic and snapshot costs are your infrastructure bill, not a vendor's line item. Star the repo on GitHub.


Sources

Related articles

Run this on infrastructure you own

bex is the open-source, AI-native Render alternative — push a git repo and get a running HTTPS service on your own machines.

Get started with bex