← Back to Engineering Notes

May 18, 2026

Our $10/mo SaaS Infrastructure

MockForge Cloud runs on three services and bills under ten dollars a month at launch traffic. Here's the entire stack, why we picked each piece, and the parts we'd change in hindsight.

The stack

Three providers do all the work:

Plus a few supporting bits: Cloudflare R2 for blob storage (uploaded specs, customer plugins), Stripe for billing, Brevo for transactional email. R2 is free under 10 GB. Stripe and Brevo are free until you have revenue and traffic respectively. Total monthly fixed cost: $0–$2.

Why each piece

Cloudflare Pages for static hosting. The admin UI is a Vite-built React SPA. Cloudflare Pages serves it from their edge, gives us free TLS, free DDoS protection, and a deploy CLI (wrangler pages deploy) that takes 45 seconds end-to-end. Their free tier is generous in a way that's actually meaningful — 500 deploys/month, 100k requests/day on the function layer if we ever need it.

We deploy from a local Makefile target rather than CI to avoid spending hosted-runner minutes on a 45-second job. make deploy-ui, done.

Fly.io for compute. Two reasons. First, the customer-mock workload is "spin up a tiny machine per customer, route by subdomain, auto-stop when idle" — Fly's primitive for this (fly machines, the auto_stop_machines config, the Fly internal-DNS routing) is a near-perfect fit, and no other provider offers an equivalent without significantly more code. Second, the per-region latency is low and the pricing is predictable: shared-cpu-1x:256MB is about $0.15/hour running, free when stopped.

The registry server itself is a single Fly machine in iad (close to Neon's database region) running the Rust binary. The machine spec is shared-cpu-1x:512MB, which is plenty until we have real load.

Neon for Postgres. Two reasons here too. The free tier scales to zero — the database compute shuts off when nothing's hitting it, which is the right shape for a launch-phase product. And the pooled connection endpoint plays nicely with SQLx's runtime-tokio-rustls setup with no special config. The cold-start latency on the first query after idle is ~500ms; for an API server with steady-ish traffic, this is invisible.

What this stack avoids

A few choices we explicitly didn't make:

What scales and what doesn't

The bill grows mostly with customer-mock traffic, not registry-server load. At 50 paying customers with mostly-idle mocks, we project ~$40-75/month. The registry server cost stays near-flat until we need multiple instances, which is a Redis-state migration we've scoped but not built yet.

Neon's free tier ends at 0.5 GB storage; their Launch plan starts around $10-15/month, and we can stay there until tens of GB. The pooled-connection endpoint means we can rotate up the connection limit without a code change.

The harder ceiling is operational, not financial. Single-instance registry server is fine until it isn't. Custom-domain TLS provisioning per customer is on the roadmap and will need Cloudflare or Fly TLS automation. These are the kinds of problems that show up at 200-500 paid customers, not 5-50.

Would we change anything

Two things, both small.

We'd put Neon in the same region as the Fly registry app from day one — we had a short stretch where the database was in a different region than the API server and every query took 80ms longer than necessary. Easy fix, embarrassing to miss.

We'd also put more thought into the customer-mock auto-stop tuning earlier. Fly's default idle timeout is generous, which is great for warm performance and bad for the cost-leak we covered in the cost-cap retrospective. Tighter defaults + the per-deployment rate limit get us closer to "pays for itself per customer" math.

Otherwise the stack does what it should: cheap when nobody's using it, scales linearly with customer traffic, doesn't require us to operate anything we shouldn't be operating at this stage.