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 item | Spec / assumption | Fly.io (monthly) |
|---|---|---|
| Web service | shared-cpu-2x, 512MB | $4.04 |
| Worker | shared-cpu-2x, 512MB | $4.04 |
| Postgres machines | 2 × shared-cpu-2x | $8.08 |
| Volumes | 2 × 50GB × $0.15/GB | $15.00 |
| Volume snapshots | ~76GB stored − 10GB free, × $0.08 | $5.28 |
| Cross-region replication | 150GB/mo × $0.006/GB (NA↔EU) | $0.90 |
| User egress | 100GB/mo × $0.02/GB | $2.00 |
| Total after Jan 1, 2026 | ≈ $39.34 | |
| Total before Jan 1, 2026 | ≈ $34.06 | |
| Delta | snapshots, 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 item | Assumption | Railway (monthly) |
|---|---|---|
| Web service | 512MB avg RAM, 0.2 avg vCPU | $9.00 |
| Worker | 512MB avg RAM, 0.2 avg vCPU | $9.00 |
| Postgres primary | 1GB RAM, 0.25 vCPU + 50GB volume | $22.50 |
| Postgres replica (2nd region) | 1GB RAM, 0.25 vCPU + 50GB volume | $22.50 |
| User egress | 100GB/mo × $0.05/GB | $5.00 |
| Cross-region private traffic | not documented | unknown |
| 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 group | Egress to internet | Private 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:
-
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.
-
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. -
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
- Fly.io Resource Pricing — volume snapshot rates ($0.08/GB-mo, 10GB free, effective January 1, 2026), volume pricing, machine pricing, granular data-transfer rates by region group.
- Fly.io Volume Snapshots docs — daily automatic snapshots, 5-day default retention, 1–60 day configuration, incremental storage billing.
- Fly.io Billing docs — pro-rated hourly snapshot billing and free-allowance handling.
- Railway Pricing and Railway pricing reference — RAM, vCPU, volume, and egress rates.
- Railway Private Networking reference and Scaling reference — multi-region replica routing; no published cross-region private-traffic pricing.



