Running Storyie on ~$25/month: our AWS + Supabase + Cloudflare cost breakdown

Storyie Engineering Team
7 min read

A detailed look at how we keep infrastructure costs near-zero for Storyie using Lambda, CloudFront, Supabase Pro, and Cloudflare R2 — with the actual numbers and the reasoning behind each architectural decision.

"Technically achievable" and "financially sustainable to keep running" are two different problems. For a solo-built product, infrastructure that costs nothing when traffic is low but scales gracefully when it grows is not a nice-to-have — it is the foundation everything else sits on.

This post covers the actual monthly cost of running Storyie in production, the reasoning behind each service choice, and the specific configuration decisions that keep the bill low.

TL;DR

Layer

Service

Monthly cost

Compute + CDN

AWS (Lambda / CloudFront / S3)

~$0.30

Database + Auth

Supabase (Pro)

$25

Image storage

Cloudflare R2

$0 (10 GB free)

Email

Resend

$0 (100/day free)

Payments

Stripe

3.6% of revenue, no fixed fee

Push notifications

Firebase Cloud Messaging

$0

Total fixed cost


~$25

Supabase Pro is the only real fixed cost. AWS runs at ~$0.30, which is almost entirely S3 storage for static assets. Everything else is either free-tier or pay-as-you-go with no baseline charge.

AWS: Lambda + CloudFront with no fixed fee

The SST v3 setup

Storyie's web app is a Next.js deployment on AWS via SST v3. The physical topology is straightforward:

CloudFront (CDN)
  ├── S3 (static assets)
  └── Lambda (SSR / API Routes)

Each layer has favorable billing characteristics at low traffic:

  • Lambda: one million requests free per month, then fractions of a cent per request
  • CloudFront: 1 TB of data transfer free (first 12 months), global CDN coverage included
  • S3: a few GB of static assets adds up to cents

In practice, Lambda and CloudFront have stayed inside their free tiers since launch. The $0.30 monthly AWS bill is S3 storage.

ARM64 and right-sized Lambda memory

Two configuration choices directly affect Lambda cost.

ARM64 (Graviton2) architecture costs about 20% less than x86 for the same workload and typically performs the same or better. In SST terms it is a one-line change:

server: {
  memory: "1024 MB",
  runtime: "nodejs22.x",
  architecture: "arm64",
  timeout: "20 seconds",
},

Right-sized memory for Cron jobs. Lambda allocates CPU proportional to memory. Running every job at the maximum tier wastes money; running everything at the minimum makes CPU-heavy jobs slow. Storyie has about 10 Cron jobs:

Job type

Memory

Schedule

Email dispatch

256 MB

Every 5–15 minutes

Aggregation / tag management

512 MB

Every 1–4 hours

Monthly AI report

1024 MB

Once per month

Matching memory to actual workload roughly halves Lambda cost compared to blanket over-provisioning.

CloudFront caching

Fewer Lambda invocations means lower cost. Cache headers do the work:

assets: {
  // Versioned JS/CSS: cache forever at the CDN
  versionedFilesCacheHeader: "public,max-age=31536000,immutable",
  // Non-versioned files: CDN TTL of one day, refresh in background
  nonVersionedFilesCacheHeader: "public,max-age=0,s-maxage=86400,stale-while-revalidate=8640",
},

Auth routes under /api/auth/* get the AWS-managed CachingDisabled policy applied explicitly. An OAuth callback cached by a CDN would silently break every login — this is the kind of bug that is obvious in retrospect and invisible until it hits a real user.

Supabase: Pro plan at $25/month

Why we started on Pro

The free tier is fine for a prototype. For a Lambda-based production deployment, it has two problems.

First, Lambda creates a new Postgres connection on every cold start. Concurrent requests stack up quickly, and the Free tier connection limit is low enough that moderate traffic produces connection errors. Starting with Pro eliminated that class of issue from day one.

Second, Free gives 500 MB of database storage. That sounds like a lot until you account for index overhead as your schema grows through migrations. Pro gives 8 GB, which is headroom we are not going to exhaust.

The third reason is data durability. Storyie stores user diaries. Losing that data is not an acceptable failure mode. Pro includes Point-in-Time Recovery; Free does not.


Free

Pro ($25/month)

Database storage

500 MB

8 GB

Backup

Daily snapshot

Point-in-Time Recovery

Bandwidth

5 GB

250 GB

File storage

1 GB

100 GB

What we actually use

We give Supabase one job: database and auth. Supabase Storage and Edge Functions are not used — those workloads go to R2 and Lambda respectively. Keeping Supabase's role narrow means we are not paying for capacity we do not need, and it keeps the dependency graph simpler.

Cloudflare R2: image storage with free egress

S3 vs R2

Egress is where image storage costs live. S3 charges $0.09 per GB transferred out; R2 charges nothing for egress. At any meaningful image delivery volume, R2 wins by a large margin.


S3

R2

Storage

$0.023/GB

$0.015/GB

Egress

$0.09/GB

$0

PUT/POST

$0.005/1000

$0.0045/1000

R2 also includes 10 GB of free storage per month, which covers us entirely for now.

Presigned URL upload pattern

Images never pass through Lambda. The flow:

Client → API Route (get presigned URL) → Client uploads directly to R2

This keeps Lambda execution time low (the API route just generates a URL and returns), avoids unnecessary data moving through the server, and lets R2 handle the heavy I/O. Cost and latency both improve.

Email, notifications, and payments

Resend

Resend's free tier covers 100 emails per day. Storyie sends welcome emails, weekly summaries, milestone notifications (first diary, 7-day streak, etc.), and monthly AI reports for Pro subscribers. At current user counts we have not hit the free tier ceiling. The paid plan is $20/month for 50,000 emails — we will cross that threshold when daily active users reach the hundreds.

Firebase Cloud Messaging

FCM is free with no practical usage limits. Push notification delivery costs nothing. The Lambda-side Cron job that checks for pending reminders every 15 minutes runs at 256 MB and fits comfortably inside the Lambda free tier.

Stripe

No fixed fee. We pay 3.6% of processed revenue (Japan rate). If revenue is zero, cost is zero. This is the right model for a product still finding its footing.

Three principles behind the cost structure

Prefer pay-as-you-go over fixed fees

Fixed-fee services charge whether you have users or not. Lambda, R2, Resend, and Stripe all have zero baseline cost. The only exception is Supabase Pro, and that is a deliberate trade-off for connection headroom and data durability — both of which have hard failure modes on the free tier.

Stack free tiers across services

No single service's free tier covers an entire app. Combined, they cover most of it:

  • Compute: Lambda (1M requests/month)
  • CDN: CloudFront (1 TB/month)
  • Images: R2 (10 GB + no egress)
  • Email: Resend (100/day)
  • Notifications: FCM (unlimited)
  • Payments: Stripe (percentage only)

The one gap is database, and that is where the $25 goes.

Do not optimize for hypothetical scale

"What if we get 100,000 users?" is a future problem. Lambda, Supabase, and R2 all have upgrade paths. Right now, the goal is to run a real production service at a cost that does not require revenue to justify. $25/month clears that bar by a comfortable margin.

Summary

The decision log, in short form:

Decision

Outcome

Lambda over EC2/ECS

No fixed compute cost

R2 over S3

Free egress for image delivery

ARM64 architecture

~20% Lambda cost reduction

Right-sized Cron memory

Avoids over-provisioning on background jobs

Supabase Pro from day one

No connection limit issues; PITR included

Supabase Storage not used

Keeps Supabase narrowly scoped to DB + Auth

Choosing infrastructure because it is cheap enough to sustain during the phase when you have no users — not because it is technically interesting — is what keeps a product running long enough to find the users. That perspective shapes every decision above.

Related Posts

Try Storyie

If you want to see what this infrastructure serves, try writing a diary at storyie.com. The iOS app opens the same content — the $25/month stack handles both sides.