On August 8, 2026, a hiking-trail app's backend doc concluded Fly.io at about $2 a month. On August 9, the same doc reversed itself: Render's free tier, $0, sleep and all. Nothing about the vendors changed overnight. The app's own architecture did, and the doc's "Revisit if" clause fired.
The doc is backend/HOSTING.md in OurHike/OurHike, and it is worth reading in full because it is the artifact vendor pricing pages can never be: a decision record. The verdict, in three lines: Render's free tier ($0, no card, sleeps after 15 minutes idle) hosts the backend. The Fly plan was deleted — fly.toml removed, no account ever created, fly deploy never run. The reason is that the safety-critical read moved to R2, so no hiker-facing path depends on the backend anymore.
What follows is a worked read of that record: its costed table refreshed to today's prices, what it optimizes that pricing pages never show, and the one row it never wrote.
The table, refreshed to October 2026
The doc preserves its August 8 costed comparison "for the reasoning, not the ranking." Here are the four rows that matter for one small backend, with October 2026 corrections:
| Row | Price today | Sleeps? | Wake cost | Gotcha |
|---|---|---|---|---|
| Render free | $0, no card | Yes, after 15 min idle | 30–60 seconds | Free Postgres is a ~30-day trial, not perpetual |
Fly.io (shared-cpu-1x 256MB, min_machines_running = 0) | ~$0.23 idle, $2.19 always-on | Yes, auto-stop | Typically 300ms–2s, app-dependent | Needs fly.toml plus flyctl; stopped rootfs bills $0.15/GB |
| Railway Hobby | $5 floor, includes $5 of usage | No | None (always on) | Usage past $5 meters on top; no scale-to-zero |
| Render Starter | $7 flat | No | None (always on) | Config lives in the dashboard unless you add render.yaml |
One number moved since August, and it moved yesterday: Fly's pricing update effective October 1, 2026 raised shared-cpu-1x with 256MB to $2.19 a month ($0.0030 an hour), up from the $1.94–2.02 the record priced. The record's Fly row is two months stale. That staleness is itself the lesson of this post, and the doc saw it coming — more on that below.
The sleep and floor facts check out across independent deploy guides. Render's free tier spins down after 15 minutes of inactivity with a first-request wake of roughly 30 seconds (the doc itself says 30–60), and Starter at $7 a month is the always-on escape hatch. Railway's Hobby plan is $5 a month including the first $5 of usage, metered past that.
What the record optimizes that pricing pages never show
Pricing pages show dollars. The record optimizes two things pricing pages cannot print: who pays the wake, and where the meter beats the floor.
Wake latency as a UX cost. The doc never prices a cold start in seconds. It prices it in who waits. When the backend served trail closures, a cold start meant a hiker walking into a closure — unacceptable, so min_machines_running = 1 was non-negotiable and every free tier was ruled out.
After the safety read moved to R2, the doc re-prices the same 30–60 seconds against the backend's remaining jobs: report submission (the offline outbox already holds it), moderation (a trusted person at a desk), photo loads, preferences. Its own sentence is the whole method: the degradation "is paid by a picture rather than by a warning, which is the whole reason it is affordable now and was not before." A pricing page says $0. The record says $0 plus exactly who waits, and why that is fine.
Floor versus meter, with the crossover worked out. Fly bills per second, so its row is not one number but a curve. Stating assumptions (256MB machine, $0.0030 an hour, ~60-second average run per wake, up to 1GB of stopped rootfs at $0.15/GB):
| Traffic pattern | Fly metered cost | Railway floor | Render Starter |
|---|---|---|---|
| Idle (50 wakes a day) | ~25 machine-hours ≈ $0.08 + $0.15 storage ≈ $0.23 | $5 | $7 |
| Low (2 running hours a day) | ~60 hours ≈ $0.18 + $0.15 ≈ $0.33 | $5 | $7 |
| Always on | 730 hours = $2.19 | $5 | $7 |
The honest crossover finding: at this size there is no crossover. The meter undercuts both floors at every traffic level — $2.19 always-on is still less than half the $5 floor — and $0 undercuts the meter. So why would anyone pick a floor? Because the floors sell what is not compute.
Railway's edge is the bundle: services plus Postgres in one project with $5 of included usage, where a realistic small service plus database lands around $6.50 by Railway's own worked example. Starter's edge is zero sleep with zero config. Price the bundle, not the row — and note the wake asymmetry the table hides: Fly wakes in typically 300ms to 2 seconds depending on app boot time, an order of magnitude under Render free's 30–60, though heavy images can stretch any platform's wake well past that.
Config surface as cost, in one line: the Render answer is "one fewer config file, one fewer CLI, and one fewer payment relationship than the Fly answer" — a host-agnostic Dockerfile that reads PORT from the environment, deploys on push, and a deliberate decision not to add render.yaml pre-emptively.
The revision is the point
The doc's first version was not wrong. Its four-step chain held end to end: the backend served closures, so a cold start on the first request after idle was unacceptable; that made always-on non-negotiable, which ruled out every free tier; of what remained, Fly was cheapest at ~$2. Every step held. Then the premise expired: verified closures started publishing to R2 daily, the client read the R2 baseline, and the live endpoint became a fallback the safety read no longer stood on.
That is when the doc's own "Revisit if" clause fired — it had been written into the first version for exactly this moment. The revision re-prices the wake against latency-tolerant jobs, deletes a plan rather than a service (fly.toml was a placeholder to the end), and lands on $0. Price lists rot; this one visibly did when Fly moved its rate card on October 1. Decision records with expiry clauses survive, because they record the premise that would change the answer, not just the answer.
The missing row: marginal cost on a box you already run
Here is the honest correction to the obvious "but Hetzner" objection: the record does price Hetzner. Its table carries a Hetzner CX22 row at ~€3.79–4.35 a month — consistent with current Hetzner Cloud list prices of ~€3.79 for the x86 CX22 and less for ARM — annotated "a whole OS to run." As a box rented for this one service, it loses to $0 Render free, and the doc is right to move on.
The row it never wrote is the marginal one: one more idle-tolerant service on a Cluster API fleet box you already pay for. That row reads ~€0 marginal — bin-pack another Deployment onto nodes running other workloads, scale it to zero with KEDA when idle, wake from a cached image with no vendor sleep cycle in the path. Scale-to-zero stops being a vendor feature with a price and becomes a scheduling choice you already own. On a flat ~€4 box, the math inverts the vendor table: the box undercuts Railway's $5 floor and Render's $7 Starter from the first service, and every additional service rides free until the box fills.
The lose-side is the doc's own annotation, and it deserves respect: a whole OS to run — node images, upgrades, the management cluster, monitoring, the 3 a.m. page. That cost amortizes to nearly nothing per service when the fleet already exists for other workloads, and dominates everything when it does not.
For one side project, the doc's veto is the correct call. For a platform already running Cluster API on owned machines, the missing row is the cheapest row in the table, and it is not close. (This is the git-push-on-owned-machines shape: the meter is the machine, so idle is a scheduling detail, not a bill.)
The line item nobody priced: Postgres
Every row above prices compute. The backend demonstrably needs Postgres — the doc names psycopg and cryptography pinned in requirements.txt and debates where alembic upgrade head runs — and no row prices the database. That omission flatters every answer unevenly.
Render's free Postgres is a trial that expires about 30 days after creation, with paid instances from ~$7 a month — so the honest always-on Render answer is $7 plus $7, $14, not $7. Deploy guides routinely pair Render's free compute with an external free Postgres to hold the total at $0, which trades the database bill for a second vendor and cross-provider latency on every query.
Add the DB row and the ranking shifts: Railway's bundled Postgres-in-one-project stops looking like a $5 floor for compute and starts looking like a floor for compute plus the database the $0 rows need anyway. Any $0-compute decision that does not price the database beside it is a draft, not a decision — including, fairly, the record this post is built on.
Write your own HOSTING.md
Steal the doc's structure, not its answer. Four items plus the database rule:
- Latency tolerance per endpoint. Name who waits on a cold start, not how many seconds it takes. If nothing safety-critical waits, free tiers are back on the table.
- Floor versus meter at your traffic. Work the metered cost at idle, low, and always-on; name the crossover traffic — or, as here, its absence — instead of comparing one flattering point.
- Config surface as cost. Count files, CLIs, payment relationships, and dashboard-versus-git settings. A setting nobody can see in a pull request drifts.
- A revisit clause with teeth. Write the premise that would change the answer, the fallback host, and keep the deploy artifact portable (a Dockerfile that reads
PORTruns anywhere these rows run). - A database row beside every compute row. Free compute with a trial database is a 30-day price, not a price.
OurHike's doc is thirteen kilobytes that price a backend, record a reversal, and expire its own premises. Most teams hold that reasoning in chat scrollback and re-derive it every outage. Write the file.
Bex.co is the open-source, AI-native Render alternative — push a git repo, get a running HTTPS service on machines you own. Star the repo on GitHub or deploy your first app today.



